📄 lcnalloc.c
字号:
/** * lcnalloc.c - Cluster (de)allocation code. Part of the Linux-NTFS project. * * Copyright (c) 2002-2004 Anton Altaparmakov * Copyright (c) 2004 Yura Pakhuchiy * * 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 Linux-NTFS * 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"/** * 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. * * This is not the prettiest function but 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 in portions of up to 8192 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 should: 1) be a full implementation of the MFT zone approach * used by Windows, 2) cause reduction in fragmentation as much as possible, * and 3) be speedy in allocations (the code is not optimized for speed, but * the algorithm is, so further speed improvements are probably possible). * * FIXME: We should be monitoring cluster allocation and increment the MFT zone * size dynamically but this is something for the future. We will just cause * heavier fragmentation by not doing it and I am not even sure Windows would * grow the MFT zone dynamically, so it might even be correct not to do this. * The overhead in doing dynamic MFT zone expansion would be very large and * unlikely worth the effort. (AIA) * * TODO: I have added in double the required zone position pointer wrap around * logic which can be optimized to having only one of the two logic sets. * However, having the double logic will work fine, but if we have only one of * the sets and we get it wrong somewhere, then we get into trouble, so * removing the duplicate logic requires _very_ careful consideration of _all_ * possible code paths. So at least for now, I am leaving the double logic - * better safe than sorry... (AIA) */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, bmp_pos, bmp_initial_pos, last_read_pos, lcn; LCN prev_lcn = 0, prev_run_len = 0, mft_zone_size; s64 clusters, br; runlist *rl = NULL, *trl; u8 *buf, *byte; int err = 0, rlpos, rlsize, buf_size; u8 pass, done_zones, search_zone, need_writeback, bit; ntfs_log_trace("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) { ntfs_log_trace("Invalid arguments!\n"); errno = EINVAL; return NULL; } /* Return empty runlist if @count == 0 */ if (!count) { rl = malloc(0x1000); if (!rl) return NULL; rl[0].vcn = start_vcn; rl[0].lcn = LCN_RL_NOT_MAPPED; rl[0].length = 0; return rl; } /* Allocate memory. */ buf = (u8*)malloc(8192); if (!buf) return NULL; /* * If no specific @start_lcn was requested, use the current data zone * position, otherwise use the requested @start_lcn but make sure it * lies outside the mft zone. Also set done_zones to 0 (no zones done) * and pass depending on whether we are starting inside a zone (1) or * at the beginning of a zone (2). If requesting from the MFT_ZONE, * we either start at the current position within the mft zone or at * the specified position. If the latter is out of bounds then we start * at the beginning of the MFT_ZONE. */ done_zones = 0; pass = 1; /* * zone_start and zone_end are the current search range. search_zone * is 1 for mft zone, 2 for data zone 1 (end of mft zone till end of * volume) and 4 for data zone 2 (start of volume till start of mft * zone). */ 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; if (!zone_start) { /* * Zone starts at beginning of volume which means a * single pass is sufficient. */ pass = 2; } } else if (zone == DATA_ZONE && zone_start >= vol->mft_zone_start && zone_start < vol->mft_zone_end) { zone_start = vol->mft_zone_end; /* * Starting at beginning of data1_zone which means a single * pass in this zone is sufficient. */ pass = 2; } else if (zone == MFT_ZONE && (zone_start < vol->mft_zone_start || zone_start >= vol->mft_zone_end)) { zone_start = vol->mft_lcn; if (!vol->mft_zone_end) zone_start = 0; /* * Starting at beginning of volume which means a single pass * is sufficient. */ pass = 2; } if (zone == MFT_ZONE) { zone_end = vol->mft_zone_end; search_zone = 1; } else /* if (zone == DATA_ZONE) */ { /* Skip searching the mft zone. */ done_zones |= 1; if (zone_start >= vol->mft_zone_end) { zone_end = vol->nr_clusters; search_zone = 2; } else { zone_end = vol->mft_zone_start; search_zone = 4; } } /* * bmp_pos is the current bit position inside the bitmap. We use * bmp_initial_pos to determine whether or not to do a zone switch. */ bmp_pos = bmp_initial_pos = zone_start; /* Loop until all clusters are allocated, i.e. clusters == 0. */ clusters = count; rlpos = rlsize = 0; while (1) { ntfs_log_trace("Start of outer while loop: done_zones = 0x%x, " "search_zone = %i, pass = %i, zone_start = " "0x%llx, zone_end = 0x%llx, bmp_initial_pos = " "0x%llx, bmp_pos = 0x%llx, rlpos = %i, rlsize = " "%i.\n", done_zones, search_zone, pass, (long long)zone_start, (long long)zone_end, (long long)bmp_initial_pos, (long long)bmp_pos, rlpos, rlsize); /* Loop until we run out of free clusters. */ last_read_pos = bmp_pos >> 3; ntfs_log_trace("last_read_pos = 0x%llx.\n", (long long)last_read_pos); br = ntfs_attr_pread(vol->lcnbmp_na, last_read_pos, 8192, buf); if (br <= 0) { if (!br) { /* Reached end of attribute. */ ntfs_log_trace("End of attribute reached. Skipping " "to zone_pass_done.\n"); goto zone_pass_done; } err = errno; ntfs_log_trace("ntfs_attr_pread() failed. Aborting.\n"); goto err_ret; } /* * We might have read less than 8192 bytes if we are close to * the end of the attribute. */ buf_size = (int)br << 3; lcn = bmp_pos & 7; bmp_pos &= ~7; need_writeback = 0; ntfs_log_trace("Before inner while loop: buf_size = %i, lcn = " "0x%llx, bmp_pos = 0x%llx, need_writeback = %i.\n", buf_size, (long long)lcn, (long long)bmp_pos, need_writeback); while (lcn < buf_size && lcn + bmp_pos < zone_end) { byte = buf + (lcn >> 3); ntfs_log_trace("In inner while loop: buf_size = %i, lcn = " "0x%llx, bmp_pos = 0x%llx, " "need_writeback = %i, byte ofs = 0x%x, " "*byte = 0x%x.\n", buf_size, (long long)lcn, (long long)bmp_pos, need_writeback, (unsigned int)(lcn >> 3), (unsigned int)*byte); /* Skip full bytes. */ if (*byte == 0xff) { lcn = (lcn + 8) & ~7; ntfs_log_trace("continuing while loop 1.\n"); continue; } bit = 1 << (lcn & 7); ntfs_log_trace("bit = %i.\n", bit); /* If the bit is already set, go onto the next one. */ if (*byte & bit) { lcn++; ntfs_log_trace("continuing while loop 2.\n"); continue; } /* Reallocate memory if necessary. */ if ((rlpos + 2) * (int)sizeof(runlist) >= rlsize) { ntfs_log_trace("Reallocating space.\n"); if (!rl) ntfs_log_trace("First free bit is at LCN = " "0x%llx.\n", (long long)(lcn + bmp_pos)); rlsize += 4096; trl = (runlist*)realloc(rl, rlsize); if (!trl) { err = ENOMEM; ntfs_log_trace("Failed to allocate memory, " "going to wb_err_ret.\n"); goto wb_err_ret; } rl = trl; ntfs_log_trace("Reallocated memory, rlsize = " "0x%x.\n", rlsize); } /* Allocate the bitmap bit. */ *byte |= bit; /* We need to write this bitmap buffer back to disk! */ need_writeback = 1; ntfs_log_trace("*byte = 0x%x, need_writeback is set.\n", (unsigned int)*byte); /* * Coalesce with previous run if adjacent LCNs. * Otherwise, append a new run. */ ntfs_log_trace("Adding run (lcn 0x%llx, len 0x%llx), " "prev_lcn = 0x%llx, lcn = 0x%llx, " "bmp_pos = 0x%llx, prev_run_len = " "0x%llx, rlpos = %i.\n", (long long)(lcn + bmp_pos), 1LL, (long long)prev_lcn, (long long)lcn, (long long)bmp_pos, (long long)prev_run_len, rlpos); if (prev_lcn == lcn + bmp_pos - prev_run_len && rlpos) { ntfs_log_trace("Coalescing to run (lcn 0x%llx, len " "0x%llx).\n", (long long)rl[rlpos - 1].lcn, (long long) rl[rlpos - 1].length); rl[rlpos - 1].length = ++prev_run_len; ntfs_log_trace("Run now (lcn 0x%llx, len 0x%llx), " "prev_run_len = 0x%llx.\n", (long long)rl[rlpos - 1].lcn, (long long)rl[rlpos - 1].length, (long long)prev_run_len); } else { if (rlpos) { ntfs_log_trace("Adding new run, (previous " "run lcn 0x%llx, len 0x%llx).\n", (long long) rl[rlpos - 1].lcn, (long long) rl[rlpos - 1].length); rl[rlpos].vcn = rl[rlpos - 1].vcn + prev_run_len; } else { ntfs_log_trace("Adding new run, is first run.\n"); rl[rlpos].vcn = start_vcn; } rl[rlpos].lcn = prev_lcn = lcn + bmp_pos; rl[rlpos].length = prev_run_len = 1; rlpos++; } /* Done? */ if (!--clusters) { LCN tc; /* * Update the current zone position. Positions * of already scanned zones have been updated * during the respective zone switches. */ tc = lcn + bmp_pos + 1; ntfs_log_trace("Done. Updating current zone " "position, tc = 0x%llx, search_zone = %i.\n", (long long)tc, search_zone); switch (search_zone) { case 1: ntfs_log_trace("Before checks, vol->mft_zone_pos = 0x%llx.\n", (long long) vol->mft_zone_pos); if (tc >= vol->mft_zone_end) { vol->mft_zone_pos = vol->mft_lcn; if (!vol->mft_zone_end) vol->mft_zone_pos = 0; } else if ((bmp_initial_pos >= vol->mft_zone_pos || tc > vol->mft_zone_pos) && tc >= vol->mft_lcn) vol->mft_zone_pos = tc; ntfs_log_trace("After checks, vol->mft_zone_pos = 0x%llx.\n", (long long) vol->mft_zone_pos); break; case 2: ntfs_log_trace("Before checks, vol->data1_zone_pos = 0x%llx.\n", (long long) vol->data1_zone_pos); if (tc >= vol->nr_clusters) vol->data1_zone_pos = vol->mft_zone_end; else if ((bmp_initial_pos >= vol->data1_zone_pos || tc > vol->data1_zone_pos) && tc >= vol->mft_zone_end) vol->data1_zone_pos = tc; ntfs_log_trace("After checks, vol->data1_zone_pos = 0x%llx.\n", (long long) vol->data1_zone_pos); break; case 4: ntfs_log_trace("Before checks, vol->data2_zone_pos = 0x%llx.\n", (long long) vol->data2_zone_pos); if (tc >= vol->mft_zone_start) vol->data2_zone_pos = 0; else if (bmp_initial_pos >= vol->data2_zone_pos || tc > vol->data2_zone_pos) vol->data2_zone_pos = tc; ntfs_log_trace("After checks, vol->data2_zone_pos = 0x%llx.\n", (long long) vol->data2_zone_pos); break; default: free(rl); free(buf); NTFS_BUG("switch (search_zone) 1"); return NULL; } ntfs_log_trace("Going to done_ret.\n"); goto done_ret; } lcn++; } bmp_pos += buf_size; ntfs_log_trace("After inner while loop: buf_size = 0x%x, lcn = " "0x%llx, bmp_pos = 0x%llx, need_writeback = %i.\n", buf_size, (long long)lcn, (long long)bmp_pos, need_writeback); if (need_writeback) { s64 bw; ntfs_log_trace("Writing back.\n"); need_writeback = 0; bw = ntfs_attr_pwrite(vol->lcnbmp_na, last_read_pos, br, buf); if (bw != br) { if (bw == -1) err = errno; else err = EIO; ntfs_log_trace("Bitmap writeback failed in read next " "buffer code path with error code %i.\n", err); goto err_ret; } } if (bmp_pos < zone_end) { ntfs_log_trace("Continuing outer while loop, bmp_pos = " "0x%llx, zone_end = 0x%llx.\n", (long long)bmp_pos, (long long)zone_end); continue; }zone_pass_done: /* Finished with the current zone pass. */ ntfs_log_trace("At zone_pass_done, pass = %i.\n", pass); if (pass == 1) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -