| /** |
| * lcnalloc.c - Cluster (de)allocation code. Originated from the Linux-NTFS project. |
| * |
| * Copyright (c) 2002-2004 Anton Altaparmakov |
| * Copyright (c) 2004 Yura Pakhuchiy |
| * Copyright (c) 2004-2008 Szabolcs Szakacsits |
| * Copyright (c) 2008-2009 Jean-Pierre Andre |
| * |
| * This program/include file is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as published |
| * by the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program/include file is distributed in the hope that it will be |
| * useful, but WITHOUT ANY WARRANTY; without even the implied warranty |
| * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program (in the main directory of the NTFS-3G |
| * distribution in the file COPYING); if not, write to the Free Software |
| * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #ifdef HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| #ifdef HAVE_STDIO_H |
| #include <stdio.h> |
| #endif |
| #ifdef HAVE_ERRNO_H |
| #include <errno.h> |
| #endif |
| |
| #include "types.h" |
| #include "attrib.h" |
| #include "bitmap.h" |
| #include "debug.h" |
| #include "runlist.h" |
| #include "volume.h" |
| #include "lcnalloc.h" |
| #include "logging.h" |
| #include "misc.h" |
| |
| /* |
| * Plenty possibilities for big optimizations all over in the cluster |
| * allocation, however at the moment the dominant bottleneck (~ 90%) is |
| * the update of the mapping pairs which converges to the cubic Faulhaber's |
| * formula as the function of the number of extents (fragments, runs). |
| */ |
| #define NTFS_LCNALLOC_BSIZE 4096 |
| #define NTFS_LCNALLOC_SKIP NTFS_LCNALLOC_BSIZE |
| |
| enum { |
| ZONE_MFT = 1, |
| ZONE_DATA1 = 2, |
| ZONE_DATA2 = 4 |
| } ; |
| |
| static void ntfs_cluster_set_zone_pos(LCN start, LCN end, LCN *pos, LCN tc) |
| { |
| ntfs_log_trace("pos: %lld tc: %lld\n", (long long)*pos, (long long)tc); |
| |
| if (tc >= end) |
| *pos = start; |
| else if (tc >= start) |
| *pos = tc; |
| } |
| |
| static void ntfs_cluster_update_zone_pos(ntfs_volume *vol, u8 zone, LCN tc) |
| { |
| ntfs_log_trace("tc = %lld, zone = %d\n", (long long)tc, zone); |
| |
| if (zone == ZONE_MFT) |
| ntfs_cluster_set_zone_pos(vol->mft_lcn, vol->mft_zone_end, |
| &vol->mft_zone_pos, tc); |
| else if (zone == ZONE_DATA1) |
| ntfs_cluster_set_zone_pos(vol->mft_zone_end, vol->nr_clusters, |
| &vol->data1_zone_pos, tc); |
| else /* zone == ZONE_DATA2 */ |
| ntfs_cluster_set_zone_pos(0, vol->mft_zone_start, |
| &vol->data2_zone_pos, tc); |
| } |
| |
| /* |
| * Unmark full zones when a cluster has been freed in a full zone |
| * |
| * Next allocation will reuse the freed cluster |
| */ |
| |
| static void update_full_status(ntfs_volume *vol, LCN lcn) |
| { |
| if (lcn >= vol->mft_zone_end) { |
| if (vol->full_zones & ZONE_DATA1) { |
| ntfs_cluster_update_zone_pos(vol, ZONE_DATA1, lcn); |
| vol->full_zones &= ~ZONE_DATA1; |
| } |
| } else |
| if (lcn < vol->mft_zone_start) { |
| if (vol->full_zones & ZONE_DATA2) { |
| ntfs_cluster_update_zone_pos(vol, ZONE_DATA2, lcn); |
| vol->full_zones &= ~ZONE_DATA2; |
| } |
| } else { |
| if (vol->full_zones & ZONE_MFT) { |
| ntfs_cluster_update_zone_pos(vol, ZONE_MFT, lcn); |
| vol->full_zones &= ~ZONE_MFT; |
| } |
| } |
| } |
| |
| static s64 max_empty_bit_range(unsigned char *buf, int size) |
| { |
| int i, j, run = 0; |
| int max_range = 0; |
| s64 start_pos = -1; |
| |
| ntfs_log_trace("Entering\n"); |
| |
| i = 0; |
| while (i < size) { |
| switch (*buf) { |
| case 0 : |
| do { |
| buf++; |
| run += 8; |
| i++; |
| } while ((i < size) && !*buf); |
| break; |
| case 255 : |
| if (run > max_range) { |
| max_range = run; |
| start_pos = (s64)i * 8 - run; |
| } |
| run = 0; |
| do { |
| buf++; |
| i++; |
| } while ((i < size) && (*buf == 255)); |
| break; |
| default : |
| for (j = 0; j < 8; j++) { |
| |
| int bit = *buf & (1 << j); |
| |
| if (bit) { |
| if (run > max_range) { |
| max_range = run; |
| start_pos = (s64)i * 8 + (j - run); |
| } |
| run = 0; |
| } else |
| run++; |
| } |
| i++; |
| buf++; |
| |
| } |
| } |
| |
| if (run > max_range) |
| start_pos = (s64)i * 8 - run; |
| |
| return start_pos; |
| } |
| |
| static int bitmap_writeback(ntfs_volume *vol, s64 pos, s64 size, void *b, |
| u8 *writeback) |
| { |
| s64 written; |
| |
| ntfs_log_trace("Entering\n"); |
| |
| if (!*writeback) |
| return 0; |
| |
| *writeback = 0; |
| |
| written = ntfs_attr_pwrite(vol->lcnbmp_na, pos, size, b); |
| if (written != size) { |
| if (!written) |
| errno = EIO; |
| ntfs_log_perror("Bitmap write error (%lld, %lld)", |
| (long long)pos, (long long)size); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * ntfs_cluster_alloc - allocate clusters on an ntfs volume |
| * @vol: mounted ntfs volume on which to allocate the clusters |
| * @start_vcn: vcn to use for the first allocated cluster |
| * @count: number of clusters to allocate |
| * @start_lcn: starting lcn at which to allocate the clusters (or -1 if none) |
| * @zone: zone from which to allocate the clusters |
| * |
| * Allocate @count clusters preferably starting at cluster @start_lcn or at the |
| * current allocator position if @start_lcn is -1, on the mounted ntfs volume |
| * @vol. @zone is either DATA_ZONE for allocation of normal clusters and |
| * MFT_ZONE for allocation of clusters for the master file table, i.e. the |
| * $MFT/$DATA attribute. |
| * |
| * On success return a runlist describing the allocated cluster(s). |
| * |
| * On error return NULL with errno set to the error code. |
| * |
| * Notes on the allocation algorithm |
| * ================================= |
| * |
| * There are two data zones. First is the area between the end of the mft zone |
| * and the end of the volume, and second is the area between the start of the |
| * volume and the start of the mft zone. On unmodified/standard NTFS 1.x |
| * volumes, the second data zone doesn't exist due to the mft zone being |
| * expanded to cover the start of the volume in order to reserve space for the |
| * mft bitmap attribute. |
| * |
| * The complexity stems from the need of implementing the mft vs data zoned |
| * approach and from the fact that we have access to the lcn bitmap via up to |
| * NTFS_LCNALLOC_BSIZE bytes at a time, so we need to cope with crossing over |
| * boundaries of two buffers. Further, the fact that the allocator allows for |
| * caller supplied hints as to the location of where allocation should begin |
| * and the fact that the allocator keeps track of where in the data zones the |
| * next natural allocation should occur, contribute to the complexity of the |
| * function. But it should all be worthwhile, because this allocator: |
| * 1) implements MFT zone reservation |
| * 2) causes reduction in fragmentation. |
| * The code is not optimized for speed. |
| */ |
| runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, |
| LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone) |
| { |
| LCN zone_start, zone_end; /* current search range */ |
| LCN last_read_pos, lcn; |
| LCN bmp_pos; /* current bit position inside the bitmap */ |
| LCN prev_lcn = 0, prev_run_len = 0; |
| s64 clusters, br; |
| runlist *rl = NULL, *trl; |
| u8 *buf, *byte, bit, writeback; |
| u8 pass = 1; /* 1: inside zone; 2: start of zone */ |
| u8 search_zone; /* 4: data2 (start) 1: mft (middle) 2: data1 (end) */ |
| u8 done_zones = 0; |
| u8 has_guess, used_zone_pos; |
| int err = 0, rlpos, rlsize, buf_size; |
| |
| ntfs_log_enter("Entering with count = 0x%llx, start_lcn = 0x%llx, " |
| "zone = %s_ZONE.\n", (long long)count, (long long) |
| start_lcn, zone == MFT_ZONE ? "MFT" : "DATA"); |
| |
| if (!vol || count < 0 || start_lcn < -1 || !vol->lcnbmp_na || |
| (s8)zone < FIRST_ZONE || zone > LAST_ZONE) { |
| errno = EINVAL; |
| ntfs_log_perror("%s: vcn: %lld, count: %lld, lcn: %lld", |
| __FUNCTION__, (long long)start_vcn, |
| (long long)count, (long long)start_lcn); |
| goto out; |
| } |
| |
| /* Return empty runlist if @count == 0 */ |
| if (!count) { |
| rl = ntfs_malloc(0x1000); |
| if (rl) { |
| rl[0].vcn = start_vcn; |
| rl[0].lcn = LCN_RL_NOT_MAPPED; |
| rl[0].length = 0; |
| } |
| goto out; |
| } |
| |
| buf = ntfs_malloc(NTFS_LCNALLOC_BSIZE); |
| if (!buf) |
| goto out; |
| /* |
| * If no @start_lcn was requested, use the current zone |
| * position otherwise use the requested @start_lcn. |
| */ |
| has_guess = 1; |
| zone_start = start_lcn; |
| |
| if (zone_start < 0) { |
| if (zone == DATA_ZONE) |
| zone_start = vol->data1_zone_pos; |
| else |
| zone_start = vol->mft_zone_pos; |
| has_guess = 0; |
| } |
| |
| used_zone_pos = has_guess ? 0 : 1; |
| |
| if (!zone_start || zone_start == vol->mft_zone_start || |
| zone_start == vol->mft_zone_end) |
| pass = 2; |
| |
| if (zone_start < vol->mft_zone_start) { |
| zone_end = vol->mft_zone_start; |
| search_zone = ZONE_DATA2; |
| } else if (zone_start < vol->mft_zone_end) { |
| zone_end = vol->mft_zone_end; |
| search_zone = ZONE_MFT; |
| } else { |
| zone_end = vol->nr_clusters; |
| search_zone = ZONE_DATA1; |
| } |
| |
| bmp_pos = zone_start; |
| |
| /* Loop until all clusters are allocated. */ |
| clusters = count; |
| rlpos = rlsize = 0; |
| while (1) { |
| /* check whether we have exhausted the current zone */ |
| if (search_zone & vol->full_zones) |
| goto zone_pass_done; |
| last_read_pos = bmp_pos >> 3; |
| br = ntfs_attr_pread(vol->lcnbmp_na, last_read_pos, |
| NTFS_LCNALLOC_BSIZE, buf); |
| if (br <= 0) { |
| if (!br) |
| goto zone_pass_done; |
| err = errno; |
| ntfs_log_perror("Reading $BITMAP failed"); |
| goto err_ret; |
| } |
| /* |
| * We might have read less than NTFS_LCNALLOC_BSIZE bytes |
| * if we are close to the end of the attribute. |
| */ |
| buf_size = (int)br << 3; |
| lcn = bmp_pos & 7; |
| bmp_pos &= ~7; |
| writeback = 0; |
| |
| while (lcn < buf_size) { |
| byte = buf + (lcn >> 3); |
| bit = 1 << (lcn & 7); |
| if (has_guess) { |
| if (*byte & bit) { |
| has_guess = 0; |
| break; |
| } |
| } else { |
| lcn = max_empty_bit_range(buf, br); |
| if (lcn < 0) |
| break; |
| has_guess = 1; |
| continue; |
| } |
| |
| /* First free bit is at lcn + bmp_pos. */ |
| |
| /* Reallocate memory if necessary. */ |
| if ((rlpos + 2) * (int)sizeof(runlist) >= rlsize) { |
| rlsize += 4096; |
| trl = realloc(rl, rlsize); |
| if (!trl) { |
| err = ENOMEM; |
| ntfs_log_perror("realloc() failed"); |
| goto wb_err_ret; |
| } |
| rl = trl; |
| } |
| |
| /* Allocate the bitmap bit. */ |
| *byte |= bit; |
| writeback = 1; |
| if (vol->free_clusters <= 0) |
| ntfs_log_error("Non-positive free clusters " |
| "(%lld)!\n", |
| (long long)vol->free_clusters); |
| else |
| vol->free_clusters--; |
| |
| /* |
| * Coalesce with previous run if adjacent LCNs. |
| * Otherwise, append a new run. |
| */ |
| if (prev_lcn == lcn + bmp_pos - prev_run_len && rlpos) { |
| ntfs_log_debug("Cluster coalesce: prev_lcn: " |
| "%lld lcn: %lld bmp_pos: %lld " |
| "prev_run_len: %lld\n", |
| (long long)prev_lcn, |
| (long long)lcn, (long long)bmp_pos, |
| (long long)prev_run_len); |
| rl[rlpos - 1].length = ++prev_run_len; |
| } else { |
| if (rlpos) |
| rl[rlpos].vcn = rl[rlpos - 1].vcn + |
| prev_run_len; |
| else { |
| rl[rlpos].vcn = start_vcn; |
| ntfs_log_debug("Start_vcn: %lld\n", |
| (long long)start_vcn); |
| } |
| |
| rl[rlpos].lcn = prev_lcn = lcn + bmp_pos; |
| rl[rlpos].length = prev_run_len = 1; |
| rlpos++; |
| } |
| |
| ntfs_log_debug("RUN: %-16lld %-16lld %-16lld\n", |
| (long long)rl[rlpos - 1].vcn, |
| (long long)rl[rlpos - 1].lcn, |
| (long long)rl[rlpos - 1].length); |
| /* Done? */ |
| if (!--clusters) { |
| if (used_zone_pos) |
| ntfs_cluster_update_zone_pos(vol, |
| search_zone, lcn + bmp_pos + 1 + |
| NTFS_LCNALLOC_SKIP); |
| goto done_ret; |
| } |
| |
| lcn++; |
| } |
| |
| if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback)) { |
| err = errno; |
| goto err_ret; |
| } |
| |
| if (!used_zone_pos) { |
| |
| used_zone_pos = 1; |
| |
| if (search_zone == ZONE_MFT) |
| zone_start = vol->mft_zone_pos; |
| else if (search_zone == ZONE_DATA1) |
| zone_start = vol->data1_zone_pos; |
| else |
| zone_start = vol->data2_zone_pos; |
| |
| if (!zone_start || zone_start == vol->mft_zone_start || |
| zone_start == vol->mft_zone_end) |
| pass = 2; |
| bmp_pos = zone_start; |
| } else |
| bmp_pos += buf_size; |
| |
| if (bmp_pos < zone_end) |
| continue; |
| |
| zone_pass_done: |
| ntfs_log_trace("Finished current zone pass(%i).\n", pass); |
| if (pass == 1) { |
| pass = 2; |
| zone_end = zone_start; |
| |
| if (search_zone == ZONE_MFT) |
| zone_start = vol->mft_zone_start; |
| else if (search_zone == ZONE_DATA1) |
| zone_start = vol->mft_zone_end; |
| else |
| zone_start = 0; |
| |
| /* Sanity check. */ |
| if (zone_end < zone_start) |
| zone_end = zone_start; |
| |
| bmp_pos = zone_start; |
| |
| continue; |
| } |
| /* pass == 2 */ |
| done_zones_check: |
| done_zones |= search_zone; |
| vol->full_zones |= search_zone; |
| if (done_zones < (ZONE_MFT + ZONE_DATA1 + ZONE_DATA2)) { |
| ntfs_log_trace("Switching zone.\n"); |
| pass = 1; |
| if (rlpos) { |
| LCN tc = rl[rlpos - 1].lcn + |
| rl[rlpos - 1].length + NTFS_LCNALLOC_SKIP; |
| |
| if (used_zone_pos) |
| ntfs_cluster_update_zone_pos(vol, |
| search_zone, tc); |
| } |
| |
| switch (search_zone) { |
| case ZONE_MFT: |
| ntfs_log_trace("Zone switch: mft -> data1\n"); |
| switch_to_data1_zone: search_zone = ZONE_DATA1; |
| zone_start = vol->data1_zone_pos; |
| zone_end = vol->nr_clusters; |
| if (zone_start == vol->mft_zone_end) |
| pass = 2; |
| break; |
| case ZONE_DATA1: |
| ntfs_log_trace("Zone switch: data1 -> data2\n"); |
| search_zone = ZONE_DATA2; |
| zone_start = vol->data2_zone_pos; |
| zone_end = vol->mft_zone_start; |
| if (!zone_start) |
| pass = 2; |
| break; |
| case ZONE_DATA2: |
| if (!(done_zones & ZONE_DATA1)) { |
| ntfs_log_trace("data2 -> data1\n"); |
| goto switch_to_data1_zone; |
| } |
| ntfs_log_trace("Zone switch: data2 -> mft\n"); |
| search_zone = ZONE_MFT; |
| zone_start = vol->mft_zone_pos; |
| zone_end = vol->mft_zone_end; |
| if (zone_start == vol->mft_zone_start) |
| pass = 2; |
| break; |
| } |
| |
| bmp_pos = zone_start; |
| |
| if (zone_start == zone_end) { |
| ntfs_log_trace("Empty zone, skipped.\n"); |
| goto done_zones_check; |
| } |
| |
| continue; |
| } |
| |
| ntfs_log_trace("All zones are finished, no space on device.\n"); |
| err = ENOSPC; |
| goto err_ret; |
| } |
| done_ret: |
| ntfs_log_debug("At done_ret.\n"); |
| /* Add runlist terminator element. */ |
| rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length; |
| rl[rlpos].lcn = LCN_RL_NOT_MAPPED; |
| rl[rlpos].length = 0; |
| if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback)) { |
| err = errno; |
| goto err_ret; |
| } |
| done_err_ret: |
| free(buf); |
| if (err) { |
| errno = err; |
| ntfs_log_perror("Failed to allocate clusters"); |
| rl = NULL; |
| } |
| out: |
| ntfs_log_leave("\n"); |
| return rl; |
| |
| wb_err_ret: |
| ntfs_log_trace("At wb_err_ret.\n"); |
| if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback)) |
| err = errno; |
| err_ret: |
| ntfs_log_trace("At err_ret.\n"); |
| if (rl) { |
| /* Add runlist terminator element. */ |
| rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length; |
| rl[rlpos].lcn = LCN_RL_NOT_MAPPED; |
| rl[rlpos].length = 0; |
| ntfs_debug_runlist_dump(rl); |
| ntfs_cluster_free_from_rl(vol, rl); |
| free(rl); |
| rl = NULL; |
| } |
| goto done_err_ret; |
| } |
| |
| /** |
| * ntfs_cluster_free_from_rl - free clusters from runlist |
| * @vol: mounted ntfs volume on which to free the clusters |
| * @rl: runlist from which deallocate clusters |
| * |
| * On success return 0 and on error return -1 with errno set to the error code. |
| */ |
| int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl) |
| { |
| s64 nr_freed = 0; |
| int ret = -1; |
| |
| ntfs_log_trace("Entering.\n"); |
| |
| for (; rl->length; rl++) { |
| |
| ntfs_log_trace("Dealloc lcn 0x%llx, len 0x%llx.\n", |
| (long long)rl->lcn, (long long)rl->length); |
| |
| if (rl->lcn >= 0) { |
| update_full_status(vol,rl->lcn); |
| if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn, |
| rl->length)) { |
| ntfs_log_perror("Cluster deallocation failed " |
| "(%lld, %lld)", |
| (long long)rl->lcn, |
| (long long)rl->length); |
| goto out; |
| } |
| nr_freed += rl->length ; |
| } |
| } |
| |
| ret = 0; |
| out: |
| vol->free_clusters += nr_freed; |
| if (vol->free_clusters > vol->nr_clusters) |
| ntfs_log_error("Too many free clusters (%lld > %lld)!", |
| (long long)vol->free_clusters, |
| (long long)vol->nr_clusters); |
| return ret; |
| } |
| |
| /* |
| * Basic cluster run free |
| * Returns 0 if successful |
| */ |
| |
| int ntfs_cluster_free_basic(ntfs_volume *vol, s64 lcn, s64 count) |
| { |
| s64 nr_freed = 0; |
| int ret = -1; |
| |
| ntfs_log_trace("Entering.\n"); |
| ntfs_log_trace("Dealloc lcn 0x%llx, len 0x%llx.\n", |
| (long long)lcn, (long long)count); |
| |
| if (lcn >= 0) { |
| update_full_status(vol,lcn); |
| if (ntfs_bitmap_clear_run(vol->lcnbmp_na, lcn, |
| count)) { |
| ntfs_log_perror("Cluster deallocation failed " |
| "(%lld, %lld)", |
| (long long)lcn, |
| (long long)count); |
| goto out; |
| } |
| nr_freed += count; |
| } |
| ret = 0; |
| out: |
| vol->free_clusters += nr_freed; |
| if (vol->free_clusters > vol->nr_clusters) |
| ntfs_log_error("Too many free clusters (%lld > %lld)!", |
| (long long)vol->free_clusters, |
| (long long)vol->nr_clusters); |
| return ret; |
| } |
| |
| /** |
| * ntfs_cluster_free - free clusters on an ntfs volume |
| * @vol: mounted ntfs volume on which to free the clusters |
| * @na: attribute whose runlist describes the clusters to free |
| * @start_vcn: vcn in @rl at which to start freeing clusters |
| * @count: number of clusters to free or -1 for all clusters |
| * |
| * Free @count clusters starting at the cluster @start_vcn in the runlist |
| * described by the attribute @na from the mounted ntfs volume @vol. |
| * |
| * If @count is -1, all clusters from @start_vcn to the end of the runlist |
| * are deallocated. |
| * |
| * On success return the number of deallocated clusters (not counting sparse |
| * clusters) and on error return -1 with errno set to the error code. |
| */ |
| int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count) |
| { |
| runlist *rl; |
| s64 delta, to_free, nr_freed = 0; |
| int ret = -1; |
| |
| if (!vol || !vol->lcnbmp_na || !na || start_vcn < 0 || |
| (count < 0 && count != -1)) { |
| ntfs_log_trace("Invalid arguments!\n"); |
| errno = EINVAL; |
| return -1; |
| } |
| |
| ntfs_log_enter("Entering for inode 0x%llx, attr 0x%x, count 0x%llx, " |
| "vcn 0x%llx.\n", (unsigned long long)na->ni->mft_no, |
| le32_to_cpu(na->type), (long long)count, (long long)start_vcn); |
| |
| rl = ntfs_attr_find_vcn(na, start_vcn); |
| if (!rl) { |
| if (errno == ENOENT) |
| ret = 0; |
| goto leave; |
| } |
| |
| if (rl->lcn < 0 && rl->lcn != LCN_HOLE) { |
| errno = EIO; |
| ntfs_log_perror("%s: Unexpected lcn (%lld)", __FUNCTION__, |
| (long long)rl->lcn); |
| goto leave; |
| } |
| |
| /* Find the starting cluster inside the run that needs freeing. */ |
| delta = start_vcn - rl->vcn; |
| |
| /* The number of clusters in this run that need freeing. */ |
| to_free = rl->length - delta; |
| if (count >= 0 && to_free > count) |
| to_free = count; |
| |
| if (rl->lcn != LCN_HOLE) { |
| /* Do the actual freeing of the clusters in this run. */ |
| update_full_status(vol,rl->lcn + delta); |
| if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn + delta, |
| to_free)) |
| goto leave; |
| nr_freed = to_free; |
| } |
| |
| /* Go to the next run and adjust the number of clusters left to free. */ |
| ++rl; |
| if (count >= 0) |
| count -= to_free; |
| |
| /* |
| * Loop over the remaining runs, using @count as a capping value, and |
| * free them. |
| */ |
| for (; rl->length && count != 0; ++rl) { |
| // FIXME: Need to try ntfs_attr_map_runlist() for attribute |
| // list support! (AIA) |
| if (rl->lcn < 0 && rl->lcn != LCN_HOLE) { |
| // FIXME: Eeek! We need rollback! (AIA) |
| errno = EIO; |
| ntfs_log_perror("%s: Invalid lcn (%lli)", |
| __FUNCTION__, (long long)rl->lcn); |
| goto out; |
| } |
| |
| /* The number of clusters in this run that need freeing. */ |
| to_free = rl->length; |
| if (count >= 0 && to_free > count) |
| to_free = count; |
| |
| if (rl->lcn != LCN_HOLE) { |
| update_full_status(vol,rl->lcn); |
| if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn, |
| to_free)) { |
| // FIXME: Eeek! We need rollback! (AIA) |
| ntfs_log_perror("%s: Clearing bitmap run failed", |
| __FUNCTION__); |
| goto out; |
| } |
| nr_freed += to_free; |
| } |
| |
| if (count >= 0) |
| count -= to_free; |
| } |
| |
| if (count != -1 && count != 0) { |
| // FIXME: Eeek! BUG() |
| errno = EIO; |
| ntfs_log_perror("%s: count still not zero (%lld)", __FUNCTION__, |
| (long long)count); |
| goto out; |
| } |
| |
| ret = nr_freed; |
| out: |
| vol->free_clusters += nr_freed ; |
| if (vol->free_clusters > vol->nr_clusters) |
| ntfs_log_error("Too many free clusters (%lld > %lld)!", |
| (long long)vol->free_clusters, |
| (long long)vol->nr_clusters); |
| leave: |
| ntfs_log_leave("\n"); |
| return ret; |
| } |