⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lcnalloc.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * 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 + -