diff options
Diffstat (limited to 'fs/ntfs/logfile.c')
| -rw-r--r-- | fs/ntfs/logfile.c | 143 |
1 files changed, 129 insertions, 14 deletions
diff --git a/fs/ntfs/logfile.c b/fs/ntfs/logfile.c index acfed325f4ec..d7932e95b1fd 100644 --- a/fs/ntfs/logfile.c +++ b/fs/ntfs/logfile.c | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * logfile.c - NTFS kernel journal handling. Part of the Linux-NTFS project. | 2 | * logfile.c - NTFS kernel journal handling. Part of the Linux-NTFS project. |
| 3 | * | 3 | * |
| 4 | * Copyright (c) 2002-2005 Anton Altaparmakov | 4 | * Copyright (c) 2002-2007 Anton Altaparmakov |
| 5 | * | 5 | * |
| 6 | * This program/include file is free software; you can redistribute it and/or | 6 | * This program/include file is free software; you can redistribute it and/or |
| 7 | * modify it under the terms of the GNU General Public License as published | 7 | * modify it under the terms of the GNU General Public License as published |
| @@ -724,24 +724,139 @@ bool ntfs_is_logfile_clean(struct inode *log_vi, const RESTART_PAGE_HEADER *rp) | |||
| 724 | */ | 724 | */ |
| 725 | bool ntfs_empty_logfile(struct inode *log_vi) | 725 | bool ntfs_empty_logfile(struct inode *log_vi) |
| 726 | { | 726 | { |
| 727 | ntfs_volume *vol = NTFS_SB(log_vi->i_sb); | 727 | VCN vcn, end_vcn; |
| 728 | ntfs_inode *log_ni = NTFS_I(log_vi); | ||
| 729 | ntfs_volume *vol = log_ni->vol; | ||
| 730 | struct super_block *sb = vol->sb; | ||
| 731 | runlist_element *rl; | ||
| 732 | unsigned long flags; | ||
| 733 | unsigned block_size, block_size_bits; | ||
| 734 | int err; | ||
| 735 | bool should_wait = true; | ||
| 728 | 736 | ||
| 729 | ntfs_debug("Entering."); | 737 | ntfs_debug("Entering."); |
| 730 | if (!NVolLogFileEmpty(vol)) { | 738 | if (NVolLogFileEmpty(vol)) { |
| 731 | int err; | 739 | ntfs_debug("Done."); |
| 732 | 740 | return true; | |
| 733 | err = ntfs_attr_set(NTFS_I(log_vi), 0, i_size_read(log_vi), | ||
| 734 | 0xff); | ||
| 735 | if (unlikely(err)) { | ||
| 736 | ntfs_error(vol->sb, "Failed to fill $LogFile with " | ||
| 737 | "0xff bytes (error code %i).", err); | ||
| 738 | return false; | ||
| 739 | } | ||
| 740 | /* Set the flag so we do not have to do it again on remount. */ | ||
| 741 | NVolSetLogFileEmpty(vol); | ||
| 742 | } | 741 | } |
| 742 | /* | ||
| 743 | * We cannot use ntfs_attr_set() because we may be still in the middle | ||
| 744 | * of a mount operation. Thus we do the emptying by hand by first | ||
| 745 | * zapping the page cache pages for the $LogFile/$DATA attribute and | ||
| 746 | * then emptying each of the buffers in each of the clusters specified | ||
| 747 | * by the runlist by hand. | ||
| 748 | */ | ||
| 749 | block_size = sb->s_blocksize; | ||
| 750 | block_size_bits = sb->s_blocksize_bits; | ||
| 751 | vcn = 0; | ||
| 752 | read_lock_irqsave(&log_ni->size_lock, flags); | ||
| 753 | end_vcn = (log_ni->initialized_size + vol->cluster_size_mask) >> | ||
| 754 | vol->cluster_size_bits; | ||
| 755 | read_unlock_irqrestore(&log_ni->size_lock, flags); | ||
| 756 | truncate_inode_pages(log_vi->i_mapping, 0); | ||
| 757 | down_write(&log_ni->runlist.lock); | ||
| 758 | rl = log_ni->runlist.rl; | ||
| 759 | if (unlikely(!rl || vcn < rl->vcn || !rl->length)) { | ||
| 760 | map_vcn: | ||
| 761 | err = ntfs_map_runlist_nolock(log_ni, vcn, NULL); | ||
| 762 | if (err) { | ||
| 763 | ntfs_error(sb, "Failed to map runlist fragment (error " | ||
| 764 | "%d).", -err); | ||
| 765 | goto err; | ||
| 766 | } | ||
| 767 | rl = log_ni->runlist.rl; | ||
| 768 | BUG_ON(!rl || vcn < rl->vcn || !rl->length); | ||
| 769 | } | ||
| 770 | /* Seek to the runlist element containing @vcn. */ | ||
| 771 | while (rl->length && vcn >= rl[1].vcn) | ||
| 772 | rl++; | ||
| 773 | do { | ||
| 774 | LCN lcn; | ||
| 775 | sector_t block, end_block; | ||
| 776 | s64 len; | ||
| 777 | |||
| 778 | /* | ||
| 779 | * If this run is not mapped map it now and start again as the | ||
| 780 | * runlist will have been updated. | ||
| 781 | */ | ||
| 782 | lcn = rl->lcn; | ||
| 783 | if (unlikely(lcn == LCN_RL_NOT_MAPPED)) { | ||
| 784 | vcn = rl->vcn; | ||
| 785 | goto map_vcn; | ||
| 786 | } | ||
| 787 | /* If this run is not valid abort with an error. */ | ||
| 788 | if (unlikely(!rl->length || lcn < LCN_HOLE)) | ||
| 789 | goto rl_err; | ||
| 790 | /* Skip holes. */ | ||
| 791 | if (lcn == LCN_HOLE) | ||
| 792 | continue; | ||
| 793 | block = lcn << vol->cluster_size_bits >> block_size_bits; | ||
| 794 | len = rl->length; | ||
| 795 | if (rl[1].vcn > end_vcn) | ||
| 796 | len = end_vcn - rl->vcn; | ||
| 797 | end_block = (lcn + len) << vol->cluster_size_bits >> | ||
| 798 | block_size_bits; | ||
| 799 | /* Iterate over the blocks in the run and empty them. */ | ||
| 800 | do { | ||
| 801 | struct buffer_head *bh; | ||
| 802 | |||
| 803 | /* Obtain the buffer, possibly not uptodate. */ | ||
| 804 | bh = sb_getblk(sb, block); | ||
| 805 | BUG_ON(!bh); | ||
| 806 | /* Setup buffer i/o submission. */ | ||
| 807 | lock_buffer(bh); | ||
| 808 | bh->b_end_io = end_buffer_write_sync; | ||
| 809 | get_bh(bh); | ||
| 810 | /* Set the entire contents of the buffer to 0xff. */ | ||
| 811 | memset(bh->b_data, -1, block_size); | ||
| 812 | if (!buffer_uptodate(bh)) | ||
| 813 | set_buffer_uptodate(bh); | ||
| 814 | if (buffer_dirty(bh)) | ||
| 815 | clear_buffer_dirty(bh); | ||
| 816 | /* | ||
| 817 | * Submit the buffer and wait for i/o to complete but | ||
| 818 | * only for the first buffer so we do not miss really | ||
| 819 | * serious i/o errors. Once the first buffer has | ||
| 820 | * completed ignore errors afterwards as we can assume | ||
| 821 | * that if one buffer worked all of them will work. | ||
| 822 | */ | ||
| 823 | submit_bh(WRITE, bh); | ||
| 824 | if (should_wait) { | ||
| 825 | should_wait = false; | ||
| 826 | wait_on_buffer(bh); | ||
| 827 | if (unlikely(!buffer_uptodate(bh))) | ||
| 828 | goto io_err; | ||
| 829 | } | ||
| 830 | brelse(bh); | ||
| 831 | } while (++block < end_block); | ||
| 832 | } while ((++rl)->vcn < end_vcn); | ||
| 833 | up_write(&log_ni->runlist.lock); | ||
| 834 | /* | ||
| 835 | * Zap the pages again just in case any got instantiated whilst we were | ||
| 836 | * emptying the blocks by hand. FIXME: We may not have completed | ||
| 837 | * writing to all the buffer heads yet so this may happen too early. | ||
| 838 | * We really should use a kernel thread to do the emptying | ||
| 839 | * asynchronously and then we can also set the volume dirty and output | ||
| 840 | * an error message if emptying should fail. | ||
| 841 | */ | ||
| 842 | truncate_inode_pages(log_vi->i_mapping, 0); | ||
| 843 | /* Set the flag so we do not have to do it again on remount. */ | ||
| 844 | NVolSetLogFileEmpty(vol); | ||
| 743 | ntfs_debug("Done."); | 845 | ntfs_debug("Done."); |
| 744 | return true; | 846 | return true; |
| 847 | io_err: | ||
| 848 | ntfs_error(sb, "Failed to write buffer. Unmount and run chkdsk."); | ||
| 849 | goto dirty_err; | ||
| 850 | rl_err: | ||
| 851 | ntfs_error(sb, "Runlist is corrupt. Unmount and run chkdsk."); | ||
| 852 | dirty_err: | ||
| 853 | NVolSetErrors(vol); | ||
| 854 | err = -EIO; | ||
| 855 | err: | ||
| 856 | up_write(&log_ni->runlist.lock); | ||
| 857 | ntfs_error(sb, "Failed to fill $LogFile with 0xff bytes (error %d).", | ||
| 858 | -err); | ||
| 859 | return false; | ||
| 745 | } | 860 | } |
| 746 | 861 | ||
| 747 | #endif /* NTFS_RW */ | 862 | #endif /* NTFS_RW */ |
