diff options
Diffstat (limited to 'fs/ntfs/lcnalloc.c')
| -rw-r--r-- | fs/ntfs/lcnalloc.c | 103 |
1 files changed, 41 insertions, 62 deletions
diff --git a/fs/ntfs/lcnalloc.c b/fs/ntfs/lcnalloc.c index 23fd911078b1..7b5934290685 100644 --- a/fs/ntfs/lcnalloc.c +++ b/fs/ntfs/lcnalloc.c | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * lcnalloc.c - Cluster (de)allocation code. Part of the Linux-NTFS project. | 2 | * lcnalloc.c - Cluster (de)allocation code. Part of the Linux-NTFS project. |
| 3 | * | 3 | * |
| 4 | * Copyright (c) 2004 Anton Altaparmakov | 4 | * Copyright (c) 2004-2005 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 |
| @@ -54,13 +54,15 @@ int ntfs_cluster_free_from_rl_nolock(ntfs_volume *vol, | |||
| 54 | int ret = 0; | 54 | int ret = 0; |
| 55 | 55 | ||
| 56 | ntfs_debug("Entering."); | 56 | ntfs_debug("Entering."); |
| 57 | if (!rl) | ||
| 58 | return 0; | ||
| 57 | for (; rl->length; rl++) { | 59 | for (; rl->length; rl++) { |
| 58 | int err; | 60 | int err; |
| 59 | 61 | ||
| 60 | if (rl->lcn < 0) | 62 | if (rl->lcn < 0) |
| 61 | continue; | 63 | continue; |
| 62 | err = ntfs_bitmap_clear_run(lcnbmp_vi, rl->lcn, rl->length); | 64 | err = ntfs_bitmap_clear_run(lcnbmp_vi, rl->lcn, rl->length); |
| 63 | if (unlikely(err && (!ret || ret == ENOMEM) && ret != err)) | 65 | if (unlikely(err && (!ret || ret == -ENOMEM) && ret != err)) |
| 64 | ret = err; | 66 | ret = err; |
| 65 | } | 67 | } |
| 66 | ntfs_debug("Done."); | 68 | ntfs_debug("Done."); |
| @@ -140,6 +142,7 @@ runlist_element *ntfs_cluster_alloc(ntfs_volume *vol, const VCN start_vcn, | |||
| 140 | LCN zone_start, zone_end, bmp_pos, bmp_initial_pos, last_read_pos, lcn; | 142 | LCN zone_start, zone_end, bmp_pos, bmp_initial_pos, last_read_pos, lcn; |
| 141 | LCN prev_lcn = 0, prev_run_len = 0, mft_zone_size; | 143 | LCN prev_lcn = 0, prev_run_len = 0, mft_zone_size; |
| 142 | s64 clusters; | 144 | s64 clusters; |
| 145 | loff_t i_size; | ||
| 143 | struct inode *lcnbmp_vi; | 146 | struct inode *lcnbmp_vi; |
| 144 | runlist_element *rl = NULL; | 147 | runlist_element *rl = NULL; |
| 145 | struct address_space *mapping; | 148 | struct address_space *mapping; |
| @@ -162,17 +165,9 @@ runlist_element *ntfs_cluster_alloc(ntfs_volume *vol, const VCN start_vcn, | |||
| 162 | BUG_ON(zone < FIRST_ZONE); | 165 | BUG_ON(zone < FIRST_ZONE); |
| 163 | BUG_ON(zone > LAST_ZONE); | 166 | BUG_ON(zone > LAST_ZONE); |
| 164 | 167 | ||
| 165 | /* Return empty runlist if @count == 0 */ | 168 | /* Return NULL if @count is zero. */ |
| 166 | // FIXME: Do we want to just return NULL instead? (AIA) | 169 | if (!count) |
| 167 | if (!count) { | 170 | return NULL; |
| 168 | rl = ntfs_malloc_nofs(PAGE_SIZE); | ||
| 169 | if (!rl) | ||
| 170 | return ERR_PTR(-ENOMEM); | ||
| 171 | rl[0].vcn = start_vcn; | ||
| 172 | rl[0].lcn = LCN_RL_NOT_MAPPED; | ||
| 173 | rl[0].length = 0; | ||
| 174 | return rl; | ||
| 175 | } | ||
| 176 | /* Take the lcnbmp lock for writing. */ | 171 | /* Take the lcnbmp lock for writing. */ |
| 177 | down_write(&vol->lcnbmp_lock); | 172 | down_write(&vol->lcnbmp_lock); |
| 178 | /* | 173 | /* |
| @@ -249,6 +244,7 @@ runlist_element *ntfs_cluster_alloc(ntfs_volume *vol, const VCN start_vcn, | |||
| 249 | clusters = count; | 244 | clusters = count; |
| 250 | rlpos = rlsize = 0; | 245 | rlpos = rlsize = 0; |
| 251 | mapping = lcnbmp_vi->i_mapping; | 246 | mapping = lcnbmp_vi->i_mapping; |
| 247 | i_size = i_size_read(lcnbmp_vi); | ||
| 252 | while (1) { | 248 | while (1) { |
| 253 | ntfs_debug("Start of outer while loop: done_zones 0x%x, " | 249 | ntfs_debug("Start of outer while loop: done_zones 0x%x, " |
| 254 | "search_zone %i, pass %i, zone_start 0x%llx, " | 250 | "search_zone %i, pass %i, zone_start 0x%llx, " |
| @@ -263,7 +259,7 @@ runlist_element *ntfs_cluster_alloc(ntfs_volume *vol, const VCN start_vcn, | |||
| 263 | last_read_pos = bmp_pos >> 3; | 259 | last_read_pos = bmp_pos >> 3; |
| 264 | ntfs_debug("last_read_pos 0x%llx.", | 260 | ntfs_debug("last_read_pos 0x%llx.", |
| 265 | (unsigned long long)last_read_pos); | 261 | (unsigned long long)last_read_pos); |
| 266 | if (last_read_pos > lcnbmp_vi->i_size) { | 262 | if (last_read_pos > i_size) { |
| 267 | ntfs_debug("End of attribute reached. " | 263 | ntfs_debug("End of attribute reached. " |
| 268 | "Skipping to zone_pass_done."); | 264 | "Skipping to zone_pass_done."); |
| 269 | goto zone_pass_done; | 265 | goto zone_pass_done; |
| @@ -287,11 +283,11 @@ runlist_element *ntfs_cluster_alloc(ntfs_volume *vol, const VCN start_vcn, | |||
| 287 | buf_size = last_read_pos & ~PAGE_CACHE_MASK; | 283 | buf_size = last_read_pos & ~PAGE_CACHE_MASK; |
| 288 | buf = page_address(page) + buf_size; | 284 | buf = page_address(page) + buf_size; |
| 289 | buf_size = PAGE_CACHE_SIZE - buf_size; | 285 | buf_size = PAGE_CACHE_SIZE - buf_size; |
| 290 | if (unlikely(last_read_pos + buf_size > lcnbmp_vi->i_size)) | 286 | if (unlikely(last_read_pos + buf_size > i_size)) |
| 291 | buf_size = lcnbmp_vi->i_size - last_read_pos; | 287 | buf_size = i_size - last_read_pos; |
| 292 | buf_size <<= 3; | 288 | buf_size <<= 3; |
| 293 | lcn = bmp_pos & 7; | 289 | lcn = bmp_pos & 7; |
| 294 | bmp_pos &= ~7; | 290 | bmp_pos &= ~(LCN)7; |
| 295 | ntfs_debug("Before inner while loop: buf_size %i, lcn 0x%llx, " | 291 | ntfs_debug("Before inner while loop: buf_size %i, lcn 0x%llx, " |
| 296 | "bmp_pos 0x%llx, need_writeback %i.", buf_size, | 292 | "bmp_pos 0x%llx, need_writeback %i.", buf_size, |
| 297 | (unsigned long long)lcn, | 293 | (unsigned long long)lcn, |
| @@ -309,7 +305,7 @@ runlist_element *ntfs_cluster_alloc(ntfs_volume *vol, const VCN start_vcn, | |||
| 309 | (unsigned int)*byte); | 305 | (unsigned int)*byte); |
| 310 | /* Skip full bytes. */ | 306 | /* Skip full bytes. */ |
| 311 | if (*byte == 0xff) { | 307 | if (*byte == 0xff) { |
| 312 | lcn = (lcn + 8) & ~7; | 308 | lcn = (lcn + 8) & ~(LCN)7; |
| 313 | ntfs_debug("Continuing while loop 1."); | 309 | ntfs_debug("Continuing while loop 1."); |
| 314 | continue; | 310 | continue; |
| 315 | } | 311 | } |
| @@ -691,7 +687,7 @@ switch_to_data1_zone: search_zone = 2; | |||
| 691 | if (zone == MFT_ZONE || mft_zone_size <= 0) { | 687 | if (zone == MFT_ZONE || mft_zone_size <= 0) { |
| 692 | ntfs_debug("No free clusters left, going to out."); | 688 | ntfs_debug("No free clusters left, going to out."); |
| 693 | /* Really no more space left on device. */ | 689 | /* Really no more space left on device. */ |
| 694 | err = ENOSPC; | 690 | err = -ENOSPC; |
| 695 | goto out; | 691 | goto out; |
| 696 | } /* zone == DATA_ZONE && mft_zone_size > 0 */ | 692 | } /* zone == DATA_ZONE && mft_zone_size > 0 */ |
| 697 | ntfs_debug("Shrinking mft zone."); | 693 | ntfs_debug("Shrinking mft zone."); |
| @@ -755,13 +751,13 @@ out: | |||
| 755 | if (rl) { | 751 | if (rl) { |
| 756 | int err2; | 752 | int err2; |
| 757 | 753 | ||
| 758 | if (err == ENOSPC) | 754 | if (err == -ENOSPC) |
| 759 | ntfs_debug("Not enough space to complete allocation, " | 755 | ntfs_debug("Not enough space to complete allocation, " |
| 760 | "err ENOSPC, first free lcn 0x%llx, " | 756 | "err -ENOSPC, first free lcn 0x%llx, " |
| 761 | "could allocate up to 0x%llx " | 757 | "could allocate up to 0x%llx " |
| 762 | "clusters.", | 758 | "clusters.", |
| 763 | (unsigned long long)rl[0].lcn, | 759 | (unsigned long long)rl[0].lcn, |
| 764 | (unsigned long long)count - clusters); | 760 | (unsigned long long)(count - clusters)); |
| 765 | /* Deallocate all allocated clusters. */ | 761 | /* Deallocate all allocated clusters. */ |
| 766 | ntfs_debug("Attempting rollback..."); | 762 | ntfs_debug("Attempting rollback..."); |
| 767 | err2 = ntfs_cluster_free_from_rl_nolock(vol, rl); | 763 | err2 = ntfs_cluster_free_from_rl_nolock(vol, rl); |
| @@ -773,10 +769,10 @@ out: | |||
| 773 | } | 769 | } |
| 774 | /* Free the runlist. */ | 770 | /* Free the runlist. */ |
| 775 | ntfs_free(rl); | 771 | ntfs_free(rl); |
| 776 | } else if (err == ENOSPC) | 772 | } else if (err == -ENOSPC) |
| 777 | ntfs_debug("No space left at all, err = ENOSPC, " | 773 | ntfs_debug("No space left at all, err = -ENOSPC, first free " |
| 778 | "first free lcn = 0x%llx.", | 774 | "lcn = 0x%llx.", |
| 779 | (unsigned long long)vol->data1_zone_pos); | 775 | (long long)vol->data1_zone_pos); |
| 780 | up_write(&vol->lcnbmp_lock); | 776 | up_write(&vol->lcnbmp_lock); |
| 781 | return ERR_PTR(err); | 777 | return ERR_PTR(err); |
| 782 | } | 778 | } |
| @@ -786,7 +782,8 @@ out: | |||
| 786 | * @vi: vfs inode whose runlist describes the clusters to free | 782 | * @vi: vfs inode whose runlist describes the clusters to free |
| 787 | * @start_vcn: vcn in the runlist of @vi at which to start freeing clusters | 783 | * @start_vcn: vcn in the runlist of @vi at which to start freeing clusters |
| 788 | * @count: number of clusters to free or -1 for all clusters | 784 | * @count: number of clusters to free or -1 for all clusters |
| 789 | * @is_rollback: if TRUE this is a rollback operation | 785 | * @write_locked: true if the runlist is locked for writing |
| 786 | * @is_rollback: true if this is a rollback operation | ||
| 790 | * | 787 | * |
| 791 | * Free @count clusters starting at the cluster @start_vcn in the runlist | 788 | * Free @count clusters starting at the cluster @start_vcn in the runlist |
| 792 | * described by the vfs inode @vi. | 789 | * described by the vfs inode @vi. |
| @@ -804,17 +801,17 @@ out: | |||
| 804 | * Return the number of deallocated clusters (not counting sparse ones) on | 801 | * Return the number of deallocated clusters (not counting sparse ones) on |
| 805 | * success and -errno on error. | 802 | * success and -errno on error. |
| 806 | * | 803 | * |
| 807 | * Locking: - The runlist described by @vi must be unlocked on entry and is | 804 | * Locking: - The runlist described by @vi must be locked on entry and is |
| 808 | * unlocked on return. | 805 | * locked on return. Note if the runlist is locked for reading the |
| 809 | * - This function takes the runlist lock of @vi for reading and | 806 | * lock may be dropped and reacquired. Note the runlist may be |
| 810 | * sometimes for writing and sometimes modifies the runlist. | 807 | * modified when needed runlist fragments need to be mapped. |
| 811 | * - The volume lcn bitmap must be unlocked on entry and is unlocked | 808 | * - The volume lcn bitmap must be unlocked on entry and is unlocked |
| 812 | * on return. | 809 | * on return. |
| 813 | * - This function takes the volume lcn bitmap lock for writing and | 810 | * - This function takes the volume lcn bitmap lock for writing and |
| 814 | * modifies the bitmap contents. | 811 | * modifies the bitmap contents. |
| 815 | */ | 812 | */ |
| 816 | s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count, | 813 | s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count, |
| 817 | const BOOL is_rollback) | 814 | const BOOL write_locked, const BOOL is_rollback) |
| 818 | { | 815 | { |
| 819 | s64 delta, to_free, total_freed, real_freed; | 816 | s64 delta, to_free, total_freed, real_freed; |
| 820 | ntfs_inode *ni; | 817 | ntfs_inode *ni; |
| @@ -846,8 +843,7 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count, | |||
| 846 | 843 | ||
| 847 | total_freed = real_freed = 0; | 844 | total_freed = real_freed = 0; |
| 848 | 845 | ||
| 849 | /* This returns with ni->runlist locked for reading on success. */ | 846 | rl = ntfs_attr_find_vcn_nolock(ni, start_vcn, write_locked); |
| 850 | rl = ntfs_find_vcn(ni, start_vcn, FALSE); | ||
| 851 | if (IS_ERR(rl)) { | 847 | if (IS_ERR(rl)) { |
| 852 | if (!is_rollback) | 848 | if (!is_rollback) |
| 853 | ntfs_error(vol->sb, "Failed to find first runlist " | 849 | ntfs_error(vol->sb, "Failed to find first runlist " |
| @@ -861,7 +857,7 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count, | |||
| 861 | ntfs_error(vol->sb, "First runlist element has " | 857 | ntfs_error(vol->sb, "First runlist element has " |
| 862 | "invalid lcn, aborting."); | 858 | "invalid lcn, aborting."); |
| 863 | err = -EIO; | 859 | err = -EIO; |
| 864 | goto unl_err_out; | 860 | goto err_out; |
| 865 | } | 861 | } |
| 866 | /* Find the starting cluster inside the run that needs freeing. */ | 862 | /* Find the starting cluster inside the run that needs freeing. */ |
| 867 | delta = start_vcn - rl->vcn; | 863 | delta = start_vcn - rl->vcn; |
| @@ -879,7 +875,7 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count, | |||
| 879 | if (!is_rollback) | 875 | if (!is_rollback) |
| 880 | ntfs_error(vol->sb, "Failed to clear first run " | 876 | ntfs_error(vol->sb, "Failed to clear first run " |
| 881 | "(error %i), aborting.", err); | 877 | "(error %i), aborting.", err); |
| 882 | goto unl_err_out; | 878 | goto err_out; |
| 883 | } | 879 | } |
| 884 | /* We have freed @to_free real clusters. */ | 880 | /* We have freed @to_free real clusters. */ |
| 885 | real_freed = to_free; | 881 | real_freed = to_free; |
| @@ -899,30 +895,15 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count, | |||
| 899 | if (unlikely(rl->lcn < LCN_HOLE)) { | 895 | if (unlikely(rl->lcn < LCN_HOLE)) { |
| 900 | VCN vcn; | 896 | VCN vcn; |
| 901 | 897 | ||
| 902 | /* | 898 | /* Attempt to map runlist. */ |
| 903 | * Attempt to map runlist, dropping runlist lock for | ||
| 904 | * the duration. | ||
| 905 | */ | ||
| 906 | vcn = rl->vcn; | 899 | vcn = rl->vcn; |
| 907 | up_read(&ni->runlist.lock); | 900 | rl = ntfs_attr_find_vcn_nolock(ni, vcn, write_locked); |
| 908 | err = ntfs_map_runlist(ni, vcn); | ||
| 909 | if (err) { | ||
| 910 | if (!is_rollback) | ||
| 911 | ntfs_error(vol->sb, "Failed to map " | ||
| 912 | "runlist fragment."); | ||
| 913 | if (err == -EINVAL || err == -ENOENT) | ||
| 914 | err = -EIO; | ||
| 915 | goto err_out; | ||
| 916 | } | ||
| 917 | /* | ||
| 918 | * This returns with ni->runlist locked for reading on | ||
| 919 | * success. | ||
| 920 | */ | ||
| 921 | rl = ntfs_find_vcn(ni, vcn, FALSE); | ||
| 922 | if (IS_ERR(rl)) { | 901 | if (IS_ERR(rl)) { |
| 923 | err = PTR_ERR(rl); | 902 | err = PTR_ERR(rl); |
| 924 | if (!is_rollback) | 903 | if (!is_rollback) |
| 925 | ntfs_error(vol->sb, "Failed to find " | 904 | ntfs_error(vol->sb, "Failed to map " |
| 905 | "runlist fragment or " | ||
| 906 | "failed to find " | ||
| 926 | "subsequent runlist " | 907 | "subsequent runlist " |
| 927 | "element."); | 908 | "element."); |
| 928 | goto err_out; | 909 | goto err_out; |
| @@ -935,7 +916,7 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count, | |||
| 935 | (unsigned long long) | 916 | (unsigned long long) |
| 936 | rl->lcn); | 917 | rl->lcn); |
| 937 | err = -EIO; | 918 | err = -EIO; |
| 938 | goto unl_err_out; | 919 | goto err_out; |
| 939 | } | 920 | } |
| 940 | } | 921 | } |
| 941 | /* The number of clusters in this run that need freeing. */ | 922 | /* The number of clusters in this run that need freeing. */ |
| @@ -951,7 +932,7 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count, | |||
| 951 | if (!is_rollback) | 932 | if (!is_rollback) |
| 952 | ntfs_error(vol->sb, "Failed to clear " | 933 | ntfs_error(vol->sb, "Failed to clear " |
| 953 | "subsequent run."); | 934 | "subsequent run."); |
| 954 | goto unl_err_out; | 935 | goto err_out; |
| 955 | } | 936 | } |
| 956 | /* We have freed @to_free real clusters. */ | 937 | /* We have freed @to_free real clusters. */ |
| 957 | real_freed += to_free; | 938 | real_freed += to_free; |
| @@ -963,7 +944,6 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count, | |||
| 963 | /* Update the total done clusters. */ | 944 | /* Update the total done clusters. */ |
| 964 | total_freed += to_free; | 945 | total_freed += to_free; |
| 965 | } | 946 | } |
| 966 | up_read(&ni->runlist.lock); | ||
| 967 | if (likely(!is_rollback)) | 947 | if (likely(!is_rollback)) |
| 968 | up_write(&vol->lcnbmp_lock); | 948 | up_write(&vol->lcnbmp_lock); |
| 969 | 949 | ||
| @@ -972,8 +952,6 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count, | |||
| 972 | /* We are done. Return the number of actually freed clusters. */ | 952 | /* We are done. Return the number of actually freed clusters. */ |
| 973 | ntfs_debug("Done."); | 953 | ntfs_debug("Done."); |
| 974 | return real_freed; | 954 | return real_freed; |
| 975 | unl_err_out: | ||
| 976 | up_read(&ni->runlist.lock); | ||
| 977 | err_out: | 955 | err_out: |
| 978 | if (is_rollback) | 956 | if (is_rollback) |
| 979 | return err; | 957 | return err; |
| @@ -987,7 +965,8 @@ err_out: | |||
| 987 | * If rollback fails, set the volume errors flag, emit an error | 965 | * If rollback fails, set the volume errors flag, emit an error |
| 988 | * message, and return the error code. | 966 | * message, and return the error code. |
| 989 | */ | 967 | */ |
| 990 | delta = __ntfs_cluster_free(vi, start_vcn, total_freed, TRUE); | 968 | delta = __ntfs_cluster_free(vi, start_vcn, total_freed, write_locked, |
| 969 | TRUE); | ||
| 991 | if (delta < 0) { | 970 | if (delta < 0) { |
| 992 | ntfs_error(vol->sb, "Failed to rollback (error %i). Leaving " | 971 | ntfs_error(vol->sb, "Failed to rollback (error %i). Leaving " |
| 993 | "inconsistent metadata! Unmount and run " | 972 | "inconsistent metadata! Unmount and run " |
