1N/A/**
1N/A * lcnalloc.c - Cluster (de)allocation code. Part of the Linux-NTFS project.
1N/A *
1N/A * Copyright (c) 2002-2004 Anton Altaparmakov
1N/A * Copyright (c) 2004 Yura Pakhuchiy
1N/A *
1N/A * This program/include file is free software; you can redistribute it and/or
1N/A * modify it under the terms of the GNU General Public License as published
1N/A * by the Free Software Foundation; either version 2 of the License, or
1N/A * (at your option) any later version.
1N/A *
1N/A * This program/include file is distributed in the hope that it will be
1N/A * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
1N/A * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1N/A * GNU General Public License for more details.
1N/A *
1N/A * You should have received a copy of the GNU General Public License
1N/A * along with this program (in the main directory of the Linux-NTFS
1N/A * distribution in the file COPYING); if not, write to the Free Software
1N/A * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1N/A */
1N/A
1N/A#ifdef HAVE_CONFIG_H
1N/A#include "config.h"
1N/A#endif
1N/A
1N/A#ifdef HAVE_STDLIB_H
1N/A#include <stdlib.h>
1N/A#endif
1N/A#ifdef HAVE_STDIO_H
1N/A#include <stdio.h>
1N/A#endif
1N/A#ifdef HAVE_ERRNO_H
1N/A#include <errno.h>
1N/A#endif
1N/A
1N/A#include "compat.h"
1N/A#include "types.h"
1N/A#include "attrib.h"
1N/A#include "bitmap.h"
1N/A#include "debug.h"
1N/A#include "runlist.h"
1N/A#include "volume.h"
1N/A#include "lcnalloc.h"
1N/A#include "logging.h"
1N/A
1N/A/**
1N/A * ntfs_cluster_alloc - allocate clusters on an ntfs volume
1N/A * @vol: mounted ntfs volume on which to allocate the clusters
1N/A * @start_vcn: vcn to use for the first allocated cluster
1N/A * @count: number of clusters to allocate
1N/A * @start_lcn: starting lcn at which to allocate the clusters (or -1 if none)
1N/A * @zone: zone from which to allocate the clusters
1N/A *
1N/A * Allocate @count clusters preferably starting at cluster @start_lcn or at the
1N/A * current allocator position if @start_lcn is -1, on the mounted ntfs volume
1N/A * @vol. @zone is either DATA_ZONE for allocation of normal clusters and
1N/A * MFT_ZONE for allocation of clusters for the master file table, i.e. the
1N/A * $MFT/$DATA attribute.
1N/A *
1N/A * On success return a runlist describing the allocated cluster(s).
1N/A *
1N/A * On error return NULL with errno set to the error code.
1N/A *
1N/A * Notes on the allocation algorithm
1N/A * =================================
1N/A *
1N/A * There are two data zones. First is the area between the end of the mft zone
1N/A * and the end of the volume, and second is the area between the start of the
1N/A * volume and the start of the mft zone. On unmodified/standard NTFS 1.x
1N/A * volumes, the second data zone doesn't exist due to the mft zone being
1N/A * expanded to cover the start of the volume in order to reserve space for the
1N/A * mft bitmap attribute.
1N/A *
1N/A * This is not the prettiest function but the complexity stems from the need of
1N/A * implementing the mft vs data zoned approach and from the fact that we have
1N/A * access to the lcn bitmap in portions of up to 8192 bytes at a time, so we
1N/A * need to cope with crossing over boundaries of two buffers. Further, the fact
1N/A * that the allocator allows for caller supplied hints as to the location of
1N/A * where allocation should begin and the fact that the allocator keeps track of
1N/A * where in the data zones the next natural allocation should occur, contribute
1N/A * to the complexity of the function. But it should all be worthwhile, because
1N/A * this allocator should: 1) be a full implementation of the MFT zone approach
1N/A * used by Windows, 2) cause reduction in fragmentation as much as possible,
1N/A * and 3) be speedy in allocations (the code is not optimized for speed, but
1N/A * the algorithm is, so further speed improvements are probably possible).
1N/A *
1N/A * FIXME: We should be monitoring cluster allocation and increment the MFT zone
1N/A * size dynamically but this is something for the future. We will just cause
1N/A * heavier fragmentation by not doing it and I am not even sure Windows would
1N/A * grow the MFT zone dynamically, so it might even be correct not to do this.
1N/A * The overhead in doing dynamic MFT zone expansion would be very large and
1N/A * unlikely worth the effort. (AIA)
1N/A *
1N/A * TODO: I have added in double the required zone position pointer wrap around
1N/A * logic which can be optimized to having only one of the two logic sets.
1N/A * However, having the double logic will work fine, but if we have only one of
1N/A * the sets and we get it wrong somewhere, then we get into trouble, so
1N/A * removing the duplicate logic requires _very_ careful consideration of _all_
1N/A * possible code paths. So at least for now, I am leaving the double logic -
1N/A * better safe than sorry... (AIA)
1N/A */
1N/Arunlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count,
1N/A LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone)
1N/A{
1N/A LCN zone_start, zone_end, bmp_pos, bmp_initial_pos, last_read_pos, lcn;
1N/A LCN prev_lcn = 0, prev_run_len = 0, mft_zone_size;
1N/A s64 clusters, br;
1N/A runlist *rl = NULL, *trl;
1N/A u8 *buf, *byte;
1N/A int err = 0, rlpos, rlsize, buf_size;
1N/A u8 pass, done_zones, search_zone, need_writeback, bit;
1N/A
1N/A ntfs_log_trace("Entering with count = 0x%llx, start_lcn = 0x%llx, zone = "
1N/A "%s_ZONE.\n", (long long)count, (long long)start_lcn,
1N/A zone == MFT_ZONE ? "MFT" : "DATA");
1N/A if (!vol || count < 0 || start_lcn < -1 || !vol->lcnbmp_na ||
1N/A (s8)zone < FIRST_ZONE || zone > LAST_ZONE) {
1N/A ntfs_log_trace("Invalid arguments!\n");
1N/A errno = EINVAL;
1N/A return NULL;
1N/A }
1N/A
1N/A /* Return empty runlist if @count == 0 */
1N/A if (!count) {
1N/A rl = ntfs_malloc(0x1000);
1N/A if (!rl)
1N/A return NULL;
1N/A rl[0].vcn = start_vcn;
1N/A rl[0].lcn = LCN_RL_NOT_MAPPED;
1N/A rl[0].length = 0;
1N/A return rl;
1N/A }
1N/A
1N/A /* Allocate memory. */
1N/A buf = (u8*)ntfs_malloc(8192);
1N/A if (!buf)
1N/A return NULL;
1N/A /*
1N/A * If no specific @start_lcn was requested, use the current data zone
1N/A * position, otherwise use the requested @start_lcn but make sure it
1N/A * lies outside the mft zone. Also set done_zones to 0 (no zones done)
1N/A * and pass depending on whether we are starting inside a zone (1) or
1N/A * at the beginning of a zone (2). If requesting from the MFT_ZONE,
1N/A * we either start at the current position within the mft zone or at
1N/A * the specified position. If the latter is out of bounds then we start
1N/A * at the beginning of the MFT_ZONE.
1N/A */
1N/A done_zones = 0;
1N/A pass = 1;
1N/A /*
1N/A * zone_start and zone_end are the current search range. search_zone
1N/A * is 1 for mft zone, 2 for data zone 1 (end of mft zone till end of
1N/A * volume) and 4 for data zone 2 (start of volume till start of mft
1N/A * zone).
1N/A */
1N/A zone_start = start_lcn;
1N/A if (zone_start < 0) {
1N/A if (zone == DATA_ZONE)
1N/A zone_start = vol->data1_zone_pos;
1N/A else
1N/A zone_start = vol->mft_zone_pos;
1N/A if (!zone_start) {
1N/A /*
1N/A * Zone starts at beginning of volume which means a
1N/A * single pass is sufficient.
1N/A */
1N/A pass = 2;
1N/A }
1N/A } else if (zone == DATA_ZONE && zone_start >= vol->mft_zone_start &&
1N/A zone_start < vol->mft_zone_end) {
1N/A zone_start = vol->mft_zone_end;
1N/A /*
1N/A * Starting at beginning of data1_zone which means a single
1N/A * pass in this zone is sufficient.
1N/A */
1N/A pass = 2;
1N/A } else if (zone == MFT_ZONE && (zone_start < vol->mft_zone_start ||
1N/A zone_start >= vol->mft_zone_end)) {
1N/A zone_start = vol->mft_lcn;
1N/A if (!vol->mft_zone_end)
1N/A zone_start = 0;
1N/A /*
1N/A * Starting at beginning of volume which means a single pass
1N/A * is sufficient.
1N/A */
1N/A pass = 2;
1N/A }
1N/A if (zone == MFT_ZONE) {
1N/A zone_end = vol->mft_zone_end;
1N/A search_zone = 1;
1N/A } else /* if (zone == DATA_ZONE) */ {
1N/A /* Skip searching the mft zone. */
1N/A done_zones |= 1;
1N/A if (zone_start >= vol->mft_zone_end) {
1N/A zone_end = vol->nr_clusters;
1N/A search_zone = 2;
1N/A } else {
1N/A zone_end = vol->mft_zone_start;
1N/A search_zone = 4;
1N/A }
1N/A }
1N/A /*
1N/A * bmp_pos is the current bit position inside the bitmap. We use
1N/A * bmp_initial_pos to determine whether or not to do a zone switch.
1N/A */
1N/A bmp_pos = bmp_initial_pos = zone_start;
1N/A
1N/A /* Loop until all clusters are allocated, i.e. clusters == 0. */
1N/A clusters = count;
1N/A rlpos = rlsize = 0;
1N/A while (1) {
1N/A ntfs_log_trace("Start of outer while loop: done_zones = 0x%x, "
1N/A "search_zone = %i, pass = %i, zone_start = "
1N/A "0x%llx, zone_end = 0x%llx, bmp_initial_pos = "
1N/A "0x%llx, bmp_pos = 0x%llx, rlpos = %i, rlsize = "
1N/A "%i.\n", done_zones, search_zone, pass,
1N/A (long long)zone_start, (long long)zone_end,
1N/A (long long)bmp_initial_pos, (long long)bmp_pos,
1N/A rlpos, rlsize);
1N/A /* Loop until we run out of free clusters. */
1N/A last_read_pos = bmp_pos >> 3;
1N/A ntfs_log_trace("last_read_pos = 0x%llx.\n", (long long)last_read_pos);
1N/A br = ntfs_attr_pread(vol->lcnbmp_na, last_read_pos, 8192, buf);
1N/A if (br <= 0) {
1N/A if (!br) {
1N/A /* Reached end of attribute. */
1N/A ntfs_log_trace("End of attribute reached. Skipping "
1N/A "to zone_pass_done.\n");
1N/A goto zone_pass_done;
1N/A }
1N/A err = errno;
1N/A ntfs_log_trace("ntfs_attr_pread() failed. Aborting.\n");
1N/A goto err_ret;
1N/A }
1N/A /*
1N/A * We might have read less than 8192 bytes if we are close to
1N/A * the end of the attribute.
1N/A */
1N/A buf_size = (int)br << 3;
1N/A lcn = bmp_pos & 7;
1N/A bmp_pos &= ~7;
1N/A need_writeback = 0;
1N/A ntfs_log_trace("Before inner while loop: buf_size = %i, lcn = "
1N/A "0x%llx, bmp_pos = 0x%llx, need_writeback = %i.\n",
1N/A buf_size, (long long)lcn, (long long)bmp_pos,
1N/A need_writeback);
1N/A while (lcn < buf_size && lcn + bmp_pos < zone_end) {
1N/A byte = buf + (lcn >> 3);
1N/A ntfs_log_trace("In inner while loop: buf_size = %i, lcn = "
1N/A "0x%llx, bmp_pos = 0x%llx, "
1N/A "need_writeback = %i, byte ofs = 0x%x, "
1N/A "*byte = 0x%x.\n", buf_size,
1N/A (long long)lcn, (long long)bmp_pos,
1N/A need_writeback, (unsigned int)(lcn >> 3),
1N/A (unsigned int)*byte);
1N/A /* Skip full bytes. */
1N/A if (*byte == 0xff) {
1N/A lcn = (lcn + 8) & ~7;
1N/A ntfs_log_trace("continuing while loop 1.\n");
1N/A continue;
1N/A }
1N/A bit = 1 << (lcn & 7);
1N/A ntfs_log_trace("bit = %i.\n", bit);
1N/A /* If the bit is already set, go onto the next one. */
1N/A if (*byte & bit) {
1N/A lcn++;
1N/A ntfs_log_trace("continuing while loop 2.\n");
1N/A continue;
1N/A }
1N/A /* Reallocate memory if necessary. */
1N/A if ((rlpos + 2) * (int)sizeof(runlist) >= rlsize) {
1N/A ntfs_log_trace("Reallocating space.\n");
1N/A if (!rl)
1N/A ntfs_log_trace("First free bit is at LCN = "
1N/A "0x%llx.\n", (long long)(lcn + bmp_pos));
1N/A rlsize += 4096;
1N/A trl = (runlist*)realloc(rl, rlsize);
1N/A if (!trl) {
1N/A err = ENOMEM;
1N/A ntfs_log_trace("Failed to allocate memory, "
1N/A "going to wb_err_ret.\n");
1N/A goto wb_err_ret;
1N/A }
1N/A rl = trl;
1N/A ntfs_log_trace("Reallocated memory, rlsize = "
1N/A "0x%x.\n", rlsize);
1N/A }
1N/A /* Allocate the bitmap bit. */
1N/A *byte |= bit;
1N/A vol->nr_free_clusters--;
1N/A /* We need to write this bitmap buffer back to disk! */
1N/A need_writeback = 1;
1N/A ntfs_log_trace("*byte = 0x%x, need_writeback is set.\n",
1N/A (unsigned int)*byte);
1N/A /*
1N/A * Coalesce with previous run if adjacent LCNs.
1N/A * Otherwise, append a new run.
1N/A */
1N/A ntfs_log_trace("Adding run (lcn 0x%llx, len 0x%llx), "
1N/A "prev_lcn = 0x%llx, lcn = 0x%llx, "
1N/A "bmp_pos = 0x%llx, prev_run_len = "
1N/A "0x%llx, rlpos = %i.\n",
1N/A (long long)(lcn + bmp_pos), 1LL,
1N/A (long long)prev_lcn, (long long)lcn,
1N/A (long long)bmp_pos,
1N/A (long long)prev_run_len, rlpos);
1N/A if (prev_lcn == lcn + bmp_pos - prev_run_len && rlpos) {
1N/A ntfs_log_trace("Coalescing to run (lcn 0x%llx, len "
1N/A "0x%llx).\n",
1N/A (long long)rl[rlpos - 1].lcn,
1N/A (long long) rl[rlpos - 1].length);
1N/A rl[rlpos - 1].length = ++prev_run_len;
1N/A ntfs_log_trace("Run now (lcn 0x%llx, len 0x%llx), "
1N/A "prev_run_len = 0x%llx.\n",
1N/A (long long)rl[rlpos - 1].lcn,
1N/A (long long)rl[rlpos - 1].length,
1N/A (long long)prev_run_len);
1N/A } else {
1N/A if (rlpos) {
1N/A ntfs_log_trace("Adding new run, (previous "
1N/A "run lcn 0x%llx, len 0x%llx).\n",
1N/A (long long) rl[rlpos - 1].lcn,
1N/A (long long) rl[rlpos - 1].length);
1N/A rl[rlpos].vcn = rl[rlpos - 1].vcn +
1N/A prev_run_len;
1N/A } else {
1N/A ntfs_log_trace("Adding new run, is first run.\n");
1N/A rl[rlpos].vcn = start_vcn;
1N/A }
1N/A rl[rlpos].lcn = prev_lcn = lcn + bmp_pos;
1N/A rl[rlpos].length = prev_run_len = 1;
1N/A rlpos++;
1N/A }
1N/A /* Done? */
1N/A if (!--clusters) {
1N/A LCN tc;
1N/A /*
1N/A * Update the current zone position. Positions
1N/A * of already scanned zones have been updated
1N/A * during the respective zone switches.
1N/A */
1N/A tc = lcn + bmp_pos + 1;
1N/A ntfs_log_trace("Done. Updating current zone "
1N/A "position, tc = 0x%llx, search_zone = %i.\n",
1N/A (long long)tc, search_zone);
1N/A switch (search_zone) {
1N/A case 1:
1N/A ntfs_log_trace("Before checks, vol->mft_zone_pos = 0x%llx.\n",
1N/A (long long) vol->mft_zone_pos);
1N/A if (tc >= vol->mft_zone_end) {
1N/A vol->mft_zone_pos =
1N/A vol->mft_lcn;
1N/A if (!vol->mft_zone_end)
1N/A vol->mft_zone_pos = 0;
1N/A } else if ((bmp_initial_pos >=
1N/A vol->mft_zone_pos ||
1N/A tc > vol->mft_zone_pos)
1N/A && tc >= vol->mft_lcn)
1N/A vol->mft_zone_pos = tc;
1N/A ntfs_log_trace("After checks, vol->mft_zone_pos = 0x%llx.\n",
1N/A (long long) vol->mft_zone_pos);
1N/A break;
1N/A case 2:
1N/A ntfs_log_trace("Before checks, vol->data1_zone_pos = 0x%llx.\n",
1N/A (long long) vol->data1_zone_pos);
1N/A if (tc >= vol->nr_clusters)
1N/A vol->data1_zone_pos =
1N/A vol->mft_zone_end;
1N/A else if ((bmp_initial_pos >=
1N/A vol->data1_zone_pos ||
1N/A tc > vol->data1_zone_pos)
1N/A && tc >= vol->mft_zone_end)
1N/A vol->data1_zone_pos = tc;
1N/A ntfs_log_trace("After checks, vol->data1_zone_pos = 0x%llx.\n",
1N/A (long long) vol->data1_zone_pos);
1N/A break;
1N/A case 4:
1N/A ntfs_log_trace("Before checks, vol->data2_zone_pos = 0x%llx.\n",
1N/A (long long) vol->data2_zone_pos);
1N/A if (tc >= vol->mft_zone_start)
1N/A vol->data2_zone_pos = 0;
1N/A else if (bmp_initial_pos >=
1N/A vol->data2_zone_pos ||
1N/A tc > vol->data2_zone_pos)
1N/A vol->data2_zone_pos = tc;
1N/A ntfs_log_trace("After checks, vol->data2_zone_pos = 0x%llx.\n",
1N/A (long long) vol->data2_zone_pos);
1N/A break;
1N/A default:
1N/A free(rl);
1N/A free(buf);
1N/A NTFS_BUG("switch (search_zone) 1");
1N/A return NULL;
1N/A }
1N/A ntfs_log_trace("Going to done_ret.\n");
1N/A goto done_ret;
1N/A }
1N/A lcn++;
1N/A }
1N/A bmp_pos += buf_size;
1N/A ntfs_log_trace("After inner while loop: buf_size = 0x%x, lcn = "
1N/A "0x%llx, bmp_pos = 0x%llx, need_writeback = %i.\n",
1N/A buf_size, (long long)lcn,
1N/A (long long)bmp_pos, need_writeback);
1N/A if (need_writeback) {
1N/A s64 bw;
1N/A ntfs_log_trace("Writing back.\n");
1N/A need_writeback = 0;
1N/A bw = ntfs_attr_pwrite(vol->lcnbmp_na, last_read_pos,
1N/A br, buf);
1N/A if (bw != br) {
1N/A if (bw == -1)
1N/A err = errno;
1N/A else
1N/A err = EIO;
1N/A ntfs_log_trace("Bitmap writeback failed in read next "
1N/A "buffer code path with error code %i.\n", err);
1N/A goto err_ret;
1N/A }
1N/A }
1N/A if (bmp_pos < zone_end) {
1N/A ntfs_log_trace("Continuing outer while loop, bmp_pos = "
1N/A "0x%llx, zone_end = 0x%llx.\n",
1N/A (long long)bmp_pos,
1N/A (long long)zone_end);
1N/A continue;
1N/A }
1N/Azone_pass_done: /* Finished with the current zone pass. */
1N/A ntfs_log_trace("At zone_pass_done, pass = %i.\n", pass);
1N/A if (pass == 1) {
1N/A /*
1N/A * Now do pass 2, scanning the first part of the zone
1N/A * we omitted in pass 1.
1N/A */
1N/A pass = 2;
1N/A zone_end = zone_start;
1N/A switch (search_zone) {
1N/A case 1: /* mft_zone */
1N/A zone_start = vol->mft_zone_start;
1N/A break;
1N/A case 2: /* data1_zone */
1N/A zone_start = vol->mft_zone_end;
1N/A break;
1N/A case 4: /* data2_zone */
1N/A zone_start = 0;
1N/A break;
1N/A default:
1N/A NTFS_BUG("switch (search_zone) 2");
1N/A }
1N/A /* Sanity check. */
1N/A if (zone_end < zone_start)
1N/A zone_end = zone_start;
1N/A bmp_pos = zone_start;
1N/A ntfs_log_trace("Continuing outer while loop, pass = 2, "
1N/A "zone_start = 0x%llx, zone_end = "
1N/A "0x%llx, bmp_pos = 0x%llx.\n",
1N/A zone_start, zone_end, bmp_pos);
1N/A continue;
1N/A } /* pass == 2 */
1N/Adone_zones_check:
1N/A ntfs_log_trace("At done_zones_check, search_zone = %i, done_zones "
1N/A "before = 0x%x, done_zones after = 0x%x.\n",
1N/A search_zone, done_zones, done_zones | search_zone);
1N/A done_zones |= search_zone;
1N/A if (done_zones < 7) {
1N/A ntfs_log_trace("Switching zone.\n");
1N/A /* Now switch to the next zone we haven't done yet. */
1N/A pass = 1;
1N/A switch (search_zone) {
1N/A case 1:
1N/A ntfs_log_trace("Switching from mft zone to data1 "
1N/A "zone.\n");
1N/A /* Update mft zone position. */
1N/A if (rlpos) {
1N/A LCN tc;
1N/A ntfs_log_trace("Before checks, vol->mft_zone_pos = 0x%llx.\n",
1N/A (long long) vol->mft_zone_pos);
1N/A tc = rl[rlpos - 1].lcn +
1N/A rl[rlpos - 1].length;
1N/A if (tc >= vol->mft_zone_end) {
1N/A vol->mft_zone_pos =
1N/A vol->mft_lcn;
1N/A if (!vol->mft_zone_end)
1N/A vol->mft_zone_pos = 0;
1N/A } else if ((bmp_initial_pos >=
1N/A vol->mft_zone_pos ||
1N/A tc > vol->mft_zone_pos)
1N/A && tc >= vol->mft_lcn)
1N/A vol->mft_zone_pos = tc;
1N/A ntfs_log_trace("After checks, vol->mft_zone_pos = 0x%llx.\n",
1N/A (long long) vol->mft_zone_pos);
1N/A }
1N/A /* Switch from mft zone to data1 zone. */
1N/Aswitch_to_data1_zone: search_zone = 2;
1N/A zone_start = bmp_initial_pos =
1N/A vol->data1_zone_pos;
1N/A zone_end = vol->nr_clusters;
1N/A if (zone_start == vol->mft_zone_end)
1N/A pass = 2;
1N/A if (zone_start >= zone_end) {
1N/A vol->data1_zone_pos = zone_start =
1N/A vol->mft_zone_end;
1N/A pass = 2;
1N/A }
1N/A break;
1N/A case 2:
1N/A ntfs_log_trace("Switching from data1 zone to data2 "
1N/A "zone.\n");
1N/A /* Update data1 zone position. */
1N/A if (rlpos) {
1N/A LCN tc;
1N/A ntfs_log_trace("Before checks, vol->data1_zone_pos = 0x%llx.\n",
1N/A (long long) vol->data1_zone_pos);
1N/A tc = rl[rlpos - 1].lcn +
1N/A rl[rlpos - 1].length;
1N/A if (tc >= vol->nr_clusters)
1N/A vol->data1_zone_pos =
1N/A vol->mft_zone_end;
1N/A else if ((bmp_initial_pos >=
1N/A vol->data1_zone_pos ||
1N/A tc > vol->data1_zone_pos)
1N/A && tc >= vol->mft_zone_end)
1N/A vol->data1_zone_pos = tc;
1N/A ntfs_log_trace("After checks, vol->data1_zone_pos = 0x%llx.\n",
1N/A (long long) vol->data1_zone_pos);
1N/A }
1N/A /* Switch from data1 zone to data2 zone. */
1N/A search_zone = 4;
1N/A zone_start = bmp_initial_pos =
1N/A vol->data2_zone_pos;
1N/A zone_end = vol->mft_zone_start;
1N/A if (!zone_start)
1N/A pass = 2;
1N/A if (zone_start >= zone_end) {
1N/A vol->data2_zone_pos = zone_start =
1N/A bmp_initial_pos = 0;
1N/A pass = 2;
1N/A }
1N/A break;
1N/A case 4:
1N/A ntfs_log_debug("Switching from data2 zone to data1 "
1N/A "zone.\n");
1N/A /* Update data2 zone position. */
1N/A if (rlpos) {
1N/A LCN tc;
1N/A ntfs_log_trace("Before checks, vol->data2_zone_pos = 0x%llx.\n",
1N/A (long long) vol->data2_zone_pos);
1N/A tc = rl[rlpos - 1].lcn +
1N/A rl[rlpos - 1].length;
1N/A if (tc >= vol->mft_zone_start)
1N/A vol->data2_zone_pos = 0;
1N/A else if (bmp_initial_pos >=
1N/A vol->data2_zone_pos ||
1N/A tc > vol->data2_zone_pos)
1N/A vol->data2_zone_pos = tc;
1N/A ntfs_log_trace("After checks, vol->data2_zone_pos = 0x%llx.\n",
1N/A (long long) vol->data2_zone_pos);
1N/A }
1N/A /* Switch from data2 zone to data1 zone. */
1N/A goto switch_to_data1_zone; /* See above. */
1N/A default:
1N/A NTFS_BUG("switch (search_zone) 3");
1N/A }
1N/A ntfs_log_trace("After zone switch, search_zone = %i, pass = "
1N/A "%i, bmp_initial_pos = 0x%llx, "
1N/A "zone_start = 0x%llx, zone_end = "
1N/A "0x%llx.\n", search_zone, pass,
1N/A (long long)bmp_initial_pos,
1N/A (long long)zone_start,
1N/A (long long)zone_end);
1N/A bmp_pos = zone_start;
1N/A if (zone_start == zone_end) {
1N/A ntfs_log_trace("Empty zone, going to "
1N/A "done_zones_check.\n");
1N/A /* Empty zone. Don't bother searching it. */
1N/A goto done_zones_check;
1N/A }
1N/A ntfs_log_trace("Continuing outer while loop.\n");
1N/A continue;
1N/A } /* done_zones == 7 */
1N/A ntfs_log_trace("All zones are finished.\n");
1N/A /*
1N/A * All zones are finished! If DATA_ZONE, shrink mft zone. If
1N/A * MFT_ZONE, we have really run out of space.
1N/A */
1N/A mft_zone_size = vol->mft_zone_end - vol->mft_zone_start;
1N/A ntfs_log_trace("vol->mft_zone_start = 0x%llx, vol->mft_zone_end = "
1N/A "0x%llx, mft_zone_size = 0x%llx.\n",
1N/A (long long)vol->mft_zone_start,
1N/A (long long)vol->mft_zone_end,
1N/A (long long)mft_zone_size);
1N/A if (zone == MFT_ZONE || mft_zone_size <= 0) {
1N/A ntfs_log_trace("No free clusters left, going to err_ret.\n");
1N/A /* Really no more space left on device. */
1N/A err = ENOSPC;
1N/A goto err_ret;
1N/A } /* zone == DATA_ZONE && mft_zone_size > 0 */
1N/A ntfs_log_trace("Shrinking mft zone.\n");
1N/A zone_end = vol->mft_zone_end;
1N/A mft_zone_size >>= 1;
1N/A if (mft_zone_size > 0)
1N/A vol->mft_zone_end = vol->mft_zone_start + mft_zone_size;
1N/A else /* mft zone and data2 zone no longer exist. */
1N/A vol->data2_zone_pos = vol->mft_zone_start =
1N/A vol->mft_zone_end = 0;
1N/A if (vol->mft_zone_pos >= vol->mft_zone_end) {
1N/A vol->mft_zone_pos = vol->mft_lcn;
1N/A if (!vol->mft_zone_end)
1N/A vol->mft_zone_pos = 0;
1N/A }
1N/A bmp_pos = zone_start = bmp_initial_pos =
1N/A vol->data1_zone_pos = vol->mft_zone_end;
1N/A search_zone = 2;
1N/A pass = 2;
1N/A done_zones &= ~2;
1N/A ntfs_log_trace("After shrinking mft zone, mft_zone_size = 0x%llx, "
1N/A "vol->mft_zone_start = 0x%llx, "
1N/A "vol->mft_zone_end = 0x%llx, vol->mft_zone_pos "
1N/A "= 0x%llx, search_zone = 2, pass = 2, "
1N/A "dones_zones = 0x%x, zone_start = 0x%llx, "
1N/A "zone_end = 0x%llx, vol->data1_zone_pos = "
1N/A "0x%llx, continuing outer while loop.\n",
1N/A (long long)mft_zone_size,
1N/A (long long)vol->mft_zone_start,
1N/A (long long)vol->mft_zone_end,
1N/A (long long)vol->mft_zone_pos,
1N/A done_zones,
1N/A (long long)zone_start,
1N/A (long long)zone_end,
1N/A (long long)vol->data1_zone_pos);
1N/A }
1N/A ntfs_log_debug("After outer while loop.\n");
1N/Adone_ret:
1N/A ntfs_log_debug("At done_ret.\n");
1N/A /* Add runlist terminator element. */
1N/A rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length;
1N/A rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
1N/A rl[rlpos].length = 0;
1N/A if (need_writeback) {
1N/A s64 bw;
1N/A ntfs_log_trace("Writing back.\n");
1N/A need_writeback = 0;
1N/A bw = ntfs_attr_pwrite(vol->lcnbmp_na, last_read_pos, br, buf);
1N/A if (bw != br) {
1N/A if (bw < 0)
1N/A err = errno;
1N/A else
1N/A err = EIO;
1N/A ntfs_log_trace("Bitmap writeback failed in done code path "
1N/A "with error code %i.\n", err);
1N/A goto err_ret;
1N/A }
1N/A }
1N/Adone_err_ret:
1N/A ntfs_log_debug("At done_err_ret (follows done_ret).\n");
1N/A free(buf);
1N/A /* Done! */
1N/A if (!err)
1N/A return rl;
1N/A ntfs_log_trace("Failed to allocate clusters. Returning with error code "
1N/A "%i.\n", err);
1N/A errno = err;
1N/A return NULL;
1N/Awb_err_ret:
1N/A ntfs_log_trace("At wb_err_ret.\n");
1N/A if (need_writeback) {
1N/A s64 bw;
1N/A ntfs_log_trace("Writing back.\n");
1N/A need_writeback = 0;
1N/A bw = ntfs_attr_pwrite(vol->lcnbmp_na, last_read_pos, br, buf);
1N/A if (bw != br) {
1N/A if (bw < 0)
1N/A err = errno;
1N/A else
1N/A err = EIO;
1N/A ntfs_log_trace("Bitmap writeback failed in error code path "
1N/A "with error code %i.\n", err);
1N/A }
1N/A }
1N/Aerr_ret:
1N/A ntfs_log_trace("At err_ret.\n");
1N/A if (rl) {
1N/A if (err == ENOSPC) {
1N/A ntfs_log_trace("err = ENOSPC, first free lcn = 0x%llx, could "
1N/A "allocate up to = 0x%llx clusters.\n",
1N/A (long long)rl[0].lcn,
1N/A (long long)count - clusters);
1N/A }
1N/A /* Add runlist terminator element. */
1N/A rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length;
1N/A rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
1N/A rl[rlpos].length = 0;
1N/A /* Deallocate all allocated clusters. */
1N/A ntfs_log_trace("Deallocating allocated clusters.\n");
1N/A ntfs_cluster_free_from_rl(vol, rl);
1N/A /* Free the runlist. */
1N/A free(rl);
1N/A rl = NULL;
1N/A } else {
1N/A if (err == ENOSPC) {
1N/A ntfs_log_trace("No space left at all, err = ENOSPC, first "
1N/A "free lcn = 0x%llx.\n",
1N/A (long long)vol->data1_zone_pos);
1N/A }
1N/A }
1N/A ntfs_log_trace("rl = NULL, going to done_err_ret.\n");
1N/A goto done_err_ret;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_cluster_free_from_rl - free clusters from runlist
1N/A * @vol: mounted ntfs volume on which to free the clusters
1N/A * @rl: runlist from which deallocate clusters
1N/A *
1N/A * On success return 0 and on error return -1 with errno set to the error code.
1N/A */
1N/Aint ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl)
1N/A{
1N/A ntfs_log_trace("Entering.\n");
1N/A
1N/A for (; rl->length; rl++) {
1N/A
1N/A ntfs_log_trace("Dealloc lcn 0x%llx, len 0x%llx.\n",
1N/A (long long)rl->lcn, (long long)rl->length);
1N/A
1N/A if (rl->lcn >= 0 && ntfs_bitmap_clear_run(vol->lcnbmp_na,
1N/A rl->lcn, rl->length)) {
1N/A int eo = errno;
1N/A ntfs_log_trace("Eeek! Deallocation of clusters failed.\n");
1N/A errno = eo;
1N/A return -1;
1N/A }
1N/A }
1N/A return 0;
1N/A}
1N/A
1N/A/**
1N/A * ntfs_cluster_free - free clusters on an ntfs volume
1N/A * @vol: mounted ntfs volume on which to free the clusters
1N/A * @na: attribute whose runlist describes the clusters to free
1N/A * @start_vcn: vcn in @rl at which to start freeing clusters
1N/A * @count: number of clusters to free or -1 for all clusters
1N/A *
1N/A * Free @count clusters starting at the cluster @start_vcn in the runlist
1N/A * described by the attribute @na from the mounted ntfs volume @vol.
1N/A *
1N/A * If @count is -1, all clusters from @start_vcn to the end of the runlist
1N/A * are deallocated.
1N/A *
1N/A * On success return the number of deallocated clusters (not counting sparse
1N/A * clusters) and on error return -1 with errno set to the error code.
1N/A */
1N/Aint ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count)
1N/A{
1N/A runlist *rl;
1N/A s64 nr_freed, delta, to_free;
1N/A
1N/A if (!vol || !vol->lcnbmp_na || !na || start_vcn < 0 ||
1N/A (count < 0 && count != -1)) {
1N/A ntfs_log_trace("Invalid arguments!\n");
1N/A errno = EINVAL;
1N/A return -1;
1N/A }
1N/A ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, count 0x%llx, "
1N/A "vcn 0x%llx.\n", (unsigned long long)na->ni->mft_no,
1N/A na->type, (long long)count, (long long)start_vcn);
1N/A
1N/A rl = ntfs_attr_find_vcn(na, start_vcn);
1N/A if (!rl) {
1N/A if (errno == ENOENT)
1N/A return 0;
1N/A else
1N/A return -1;
1N/A }
1N/A
1N/A if (rl->lcn < 0 && rl->lcn != LCN_HOLE) {
1N/A errno = EIO;
1N/A return -1;
1N/A }
1N/A
1N/A /* Find the starting cluster inside the run that needs freeing. */
1N/A delta = start_vcn - rl->vcn;
1N/A
1N/A /* The number of clusters in this run that need freeing. */
1N/A to_free = rl->length - delta;
1N/A if (count >= 0 && to_free > count)
1N/A to_free = count;
1N/A
1N/A if (rl->lcn != LCN_HOLE) {
1N/A /* Do the actual freeing of the clusters in this run. */
1N/A if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn + delta,
1N/A to_free))
1N/A return -1;
1N/A /* We have freed @to_free real clusters. */
1N/A nr_freed = to_free;
1N/A } else {
1N/A /* No real clusters were freed. */
1N/A nr_freed = 0;
1N/A }
1N/A
1N/A /* Go to the next run and adjust the number of clusters left to free. */
1N/A ++rl;
1N/A if (count >= 0)
1N/A count -= to_free;
1N/A
1N/A /*
1N/A * Loop over the remaining runs, using @count as a capping value, and
1N/A * free them.
1N/A */
1N/A for (; rl->length && count != 0; ++rl) {
1N/A // FIXME: Need to try ntfs_attr_map_runlist() for attribute
1N/A // list support! (AIA)
1N/A if (rl->lcn < 0 && rl->lcn != LCN_HOLE) {
1N/A // FIXME: Eeek! We need rollback! (AIA)
1N/A ntfs_log_trace("Eeek! invalid lcn (= %lli). Should attempt "
1N/A "to map runlist! Leaving inconsistent "
1N/A "metadata!\n", (long long)rl->lcn);
1N/A errno = EIO;
1N/A return -1;
1N/A }
1N/A
1N/A /* The number of clusters in this run that need freeing. */
1N/A to_free = rl->length;
1N/A if (count >= 0 && to_free > count)
1N/A to_free = count;
1N/A
1N/A if (rl->lcn != LCN_HOLE) {
1N/A /* Do the actual freeing of the clusters in the run. */
1N/A if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn,
1N/A to_free)) {
1N/A int eo = errno;
1N/A
1N/A // FIXME: Eeek! We need rollback! (AIA)
1N/A ntfs_log_trace("Eeek! bitmap clear run failed. "
1N/A "Leaving inconsistent metadata!\n");
1N/A errno = eo;
1N/A return -1;
1N/A }
1N/A /* We have freed @to_free real clusters. */
1N/A nr_freed += to_free;
1N/A }
1N/A
1N/A if (count >= 0)
1N/A count -= to_free;
1N/A }
1N/A
1N/A if (count != -1 && count != 0) {
1N/A // FIXME: Eeek! BUG()
1N/A ntfs_log_trace("Eeek! count still not zero (= %lli). Leaving "
1N/A "inconsistent metadata!\n", (long long)count);
1N/A errno = EIO;
1N/A return -1;
1N/A }
1N/A
1N/A /* Done. Return the number of actual clusters that were freed. */
1N/A return nr_freed;
1N/A}