📄 lcnalloc.c
字号:
/* * lcnalloc.c - Cluster (de)allocation code. Part of the Linux-NTFS project. * * Copyright (c) 2004-2005 Anton Altaparmakov * * 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 NTFS_RW#include <linux/pagemap.h>#include "lcnalloc.h"#include "debug.h"#include "bitmap.h"#include "inode.h"#include "volume.h"#include "attrib.h"#include "malloc.h"#include "aops.h"#include "ntfs.h"/** * ntfs_cluster_free_from_rl_nolock - free clusters from runlist * @vol: mounted ntfs volume on which to free the clusters * @rl: runlist describing the clusters to free * * Free all the clusters described by the runlist @rl on the volume @vol. In * the case of an error being returned, at least some of the clusters were not * freed. * * Return 0 on success and -errno on error. * * Locking: - The volume lcn bitmap must be locked for writing on entry and is * left locked on return. */int ntfs_cluster_free_from_rl_nolock(ntfs_volume *vol, const runlist_element *rl){ struct inode *lcnbmp_vi = vol->lcnbmp_ino; int ret = 0; ntfs_debug("Entering."); if (!rl) return 0; for (; rl->length; rl++) { int err; if (rl->lcn < 0) continue; err = ntfs_bitmap_clear_run(lcnbmp_vi, rl->lcn, rl->length); if (unlikely(err && (!ret || ret == -ENOMEM) && ret != err)) ret = err; } ntfs_debug("Done."); return ret;}/** * 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 * @is_extension: if 'true', this is an attribute extension * * 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 or * MFT_ZONE for allocation of clusters for the master file table, i.e. the * $MFT/$DATA attribute. * * @start_vcn specifies the vcn of the first allocated cluster. This makes * merging the resulting runlist with the old runlist easier. * * If @is_extension is 'true', the caller is allocating clusters to extend an * attribute and if it is 'false', the caller is allocating clusters to fill a * hole in an attribute. Practically the difference is that if @is_extension * is 'true' the returned runlist will be terminated with LCN_ENOENT and if * @is_extension is 'false' the runlist will be terminated with * LCN_RL_NOT_MAPPED. * * You need to check the return value with IS_ERR(). If this is false, the * function was successful and the return value is a runlist describing the * allocated cluster(s). If IS_ERR() is true, the function failed and * PTR_ERR() gives you 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 does not 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 NT, 2) cause reduction in * fragmentation, 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) * * Locking: - The volume lcn bitmap must be unlocked on entry and is unlocked * on return. * - This function takes the volume lcn bitmap lock for writing and * modifies the bitmap contents. */runlist_element *ntfs_cluster_alloc(ntfs_volume *vol, const VCN start_vcn, const s64 count, const LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone, const bool is_extension){ 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; loff_t i_size; struct inode *lcnbmp_vi; runlist_element *rl = NULL; struct address_space *mapping; struct page *page = NULL; u8 *buf, *byte; int err = 0, rlpos, rlsize, buf_size; u8 pass, done_zones, search_zone, need_writeback = 0, bit; ntfs_debug("Entering for start_vcn 0x%llx, count 0x%llx, start_lcn " "0x%llx, zone %s_ZONE.", (unsigned long long)start_vcn, (unsigned long long)count, (unsigned long long)start_lcn, zone == MFT_ZONE ? "MFT" : "DATA"); BUG_ON(!vol); lcnbmp_vi = vol->lcnbmp_ino; BUG_ON(!lcnbmp_vi); BUG_ON(start_vcn < 0); BUG_ON(count < 0); BUG_ON(start_lcn < -1); BUG_ON(zone < FIRST_ZONE); BUG_ON(zone > LAST_ZONE); /* Return NULL if @count is zero. */ if (!count) return NULL; /* Take the lcnbmp lock for writing. */ down_write(&vol->lcnbmp_lock); /* * 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; mapping = lcnbmp_vi->i_mapping; i_size = i_size_read(lcnbmp_vi); while (1) { ntfs_debug("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.", done_zones, search_zone, pass, (unsigned long long)zone_start, (unsigned long long)zone_end, (unsigned long long)bmp_initial_pos, (unsigned long long)bmp_pos, rlpos, rlsize); /* Loop until we run out of free clusters. */ last_read_pos = bmp_pos >> 3; ntfs_debug("last_read_pos 0x%llx.", (unsigned long long)last_read_pos); if (last_read_pos > i_size) { ntfs_debug("End of attribute reached. " "Skipping to zone_pass_done."); goto zone_pass_done; } if (likely(page)) { if (need_writeback) { ntfs_debug("Marking page dirty."); flush_dcache_page(page); set_page_dirty(page); need_writeback = 0; } ntfs_unmap_page(page); } page = ntfs_map_page(mapping, last_read_pos >> PAGE_CACHE_SHIFT); if (IS_ERR(page)) { err = PTR_ERR(page); ntfs_error(vol->sb, "Failed to map page."); goto out; } buf_size = last_read_pos & ~PAGE_CACHE_MASK; buf = page_address(page) + buf_size; buf_size = PAGE_CACHE_SIZE - buf_size; if (unlikely(last_read_pos + buf_size > i_size)) buf_size = i_size - last_read_pos; buf_size <<= 3; lcn = bmp_pos & 7; bmp_pos &= ~(LCN)7; ntfs_debug("Before inner while loop: buf_size %i, lcn 0x%llx, " "bmp_pos 0x%llx, need_writeback %i.", buf_size, (unsigned long long)lcn, (unsigned long long)bmp_pos, need_writeback); while (lcn < buf_size && lcn + bmp_pos < zone_end) { byte = buf + (lcn >> 3); ntfs_debug("In inner while loop: buf_size %i, " "lcn 0x%llx, bmp_pos 0x%llx, " "need_writeback %i, byte ofs 0x%x, " "*byte 0x%x.", buf_size, (unsigned long long)lcn, (unsigned long long)bmp_pos, need_writeback, (unsigned int)(lcn >> 3), (unsigned int)*byte); /* Skip full bytes. */ if (*byte == 0xff) { lcn = (lcn + 8) & ~(LCN)7; ntfs_debug("Continuing while loop 1."); continue; } bit = 1 << (lcn & 7); ntfs_debug("bit 0x%x.", bit); /* If the bit is already set, go onto the next one. */ if (*byte & bit) { lcn++; ntfs_debug("Continuing while loop 2."); continue; } /* * Allocate more memory if needed, including space for * the terminator element. * ntfs_malloc_nofs() operates on whole pages only. */ if ((rlpos + 2) * sizeof(*rl) > rlsize) { runlist_element *rl2; ntfs_debug("Reallocating memory."); if (!rl) ntfs_debug("First free bit is at LCN "
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -