1055 lines · 32.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * Cluster (de)allocation code. * * Copyright (c) 2004-2005 Anton Altaparmakov * Copyright (c) 2025 LG Electronics Co., Ltd. * * Part of this file is based on code from the NTFS-3G. * and is copyrighted by the respective authors below: * Copyright (c) 2002-2004 Anton Altaparmakov * Copyright (c) 2004 Yura Pakhuchiy * Copyright (c) 2004-2008 Szabolcs Szakacsits * Copyright (c) 2008-2009 Jean-Pierre Andre */ #include <linux/blkdev.h> #include "lcnalloc.h"#include "bitmap.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(struct ntfs_volume *vol,		const struct runlist_element *rl){	struct inode *lcnbmp_vi = vol->lcnbmp_ino;	int ret = 0;	s64 nr_freed = 0; 	ntfs_debug("Entering.");	if (!rl)		return 0; 	if (!NVolFreeClusterKnown(vol))		wait_event(vol->free_waitq, NVolFreeClusterKnown(vol)); 	for (; rl->length; rl++) {		int err; 		if (rl->lcn < 0)			continue;		err = ntfs_bitmap_clear_run(lcnbmp_vi, rl->lcn, rl->length);		if (likely(!err))			nr_freed += rl->length;		else if (!ret || ret == -ENOMEM)			ret = err;	}	ntfs_inc_free_clusters(vol, nr_freed);	ntfs_debug("Done.");	return ret;} 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_debug("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;} /* * ntfs_cluster_alloc - allocate clusters on an ntfs volume * @vol:		mounted ntfs volume on which to allocate clusters * @start_vcn:		vcn of 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 (MFT_ZONE or DATA_ZONE) * @is_extension:	if true, the caller is extending an attribute * @is_contig:		if true, require contiguous allocation * @is_dealloc:		if true, the allocation is for deallocation purposes * * 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). * * 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. * * Return: Runlist describing the allocated cluster(s) on success, error pointer *         on failure. */struct runlist_element *ntfs_cluster_alloc(struct ntfs_volume *vol, const s64 start_vcn,		const s64 count, const s64 start_lcn,		const int zone,		const bool is_extension,		const bool is_contig,		const bool is_dealloc){	s64 zone_start, zone_end, bmp_pos, bmp_initial_pos, last_read_pos, lcn;	s64 prev_lcn = 0, prev_run_len = 0, mft_zone_size;	s64 clusters, free_clusters;	loff_t i_size;	struct inode *lcnbmp_vi;	struct runlist_element *rl = NULL;	struct address_space *mapping;	struct folio *folio = NULL;	u8 *buf = NULL, *byte;	int err = 0, rlpos, rlsize, buf_size, pg_off;	u8 pass, done_zones, search_zone, need_writeback = 0, bit;	unsigned int memalloc_flags;	u8 has_guess, used_zone_pos;	pgoff_t index; 	ntfs_debug("Entering for start_vcn 0x%llx, count 0x%llx, start_lcn 0x%llx, zone %s_ZONE.",			start_vcn, count, start_lcn,			zone == MFT_ZONE ? "MFT" : "DATA"); 	lcnbmp_vi = vol->lcnbmp_ino;	if (start_vcn < 0 || start_lcn < LCN_HOLE ||	    zone < FIRST_ZONE || zone > LAST_ZONE)		return ERR_PTR(-EINVAL); 	/* Return NULL if @count is zero. */	if (count < 0 || !count)		return ERR_PTR(-EINVAL); 	memalloc_flags = memalloc_nofs_save(); 	if (!NVolFreeClusterKnown(vol))		wait_event(vol->free_waitq, NVolFreeClusterKnown(vol));	free_clusters = atomic64_read(&vol->free_clusters); 	/* Take the lcnbmp lock for writing. */	down_write(&vol->lcnbmp_lock);	if (is_dealloc == false)		free_clusters -= atomic64_read(&vol->dirty_clusters); 	if (free_clusters < count) {		err = -ENOSPC;		goto out_restore;	} 	/*	 * 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).	 */	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;		if (!zone_start) {			/*			 * Zone starts at beginning of volume which means a			 * single pass is sufficient.			 */			pass = 2;		}		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 = 4;		/* Skip searching the mft zone. */		done_zones |= 1;	} else if (zone_start < vol->mft_zone_end) {		zone_end = vol->mft_zone_end;		search_zone = 1;	} else {		zone_end = vol->nr_clusters;		search_zone = 2;		/* Skip searching the mft zone. */		done_zones |= 1;	} 	/*	 * 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;	/*	 * lcn_empty_bits_per_page is sized from nr_clusters, but $Bitmap can	 * cover more clusters than that; bound the scan by the array.	 */	i_size = min_t(s64, i_size_read(lcnbmp_vi),		       ((s64)vol->nr_clusters + 7) >> 3);	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,				zone_start, zone_end, bmp_initial_pos,				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.", 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(folio)) {			if (need_writeback) {				ntfs_debug("Marking page dirty.");				folio_mark_dirty(folio);				need_writeback = 0;			}			folio_unlock(folio);			kunmap_local(buf);			folio_put(folio);			folio = NULL;		} 		index = last_read_pos >> PAGE_SHIFT;		pg_off = last_read_pos & ~PAGE_MASK;		buf_size = PAGE_SIZE - pg_off;		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 &= ~(s64)7; 		if (vol->lcn_empty_bits_per_page[index] == 0)			goto next_bmp_pos; 		folio = read_mapping_folio(mapping, index, NULL);		if (IS_ERR(folio)) {			err = PTR_ERR(folio);			ntfs_error(vol->sb, "Failed to map page.");			goto out;		} 		folio_lock(folio);		buf = kmap_local_folio(folio, 0) + pg_off;		ntfs_debug("Before inner while loop: buf_size %i, lcn 0x%llx, bmp_pos 0x%llx, need_writeback %i.",				buf_size, lcn, 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, lcn, bmp_pos, need_writeback,					(unsigned int)(lcn >> 3),					(unsigned int)*byte);			bit = 1 << (lcn & 7);			ntfs_debug("bit 0x%x.", bit); 			if (has_guess) {				if (*byte & bit) {					if (is_contig == true && prev_run_len > 0)						goto done; 					has_guess = 0;					break;				}			} else {				lcn = max_empty_bit_range(buf, buf_size >> 3);				if (lcn < 0)					break;				has_guess = 1;				continue;			}			/*			 * Allocate more memory if needed, including space for			 * the terminator element.			 * kvzalloc() operates on whole pages only.			 */			if ((rlpos + 2) * sizeof(*rl) > rlsize) {				struct runlist_element *rl2; 				ntfs_debug("Reallocating memory.");				if (!rl)					ntfs_debug("First free bit is at s64 0x%llx.",							lcn + bmp_pos);				rl2 = kvzalloc(rlsize + PAGE_SIZE, GFP_NOFS);				if (unlikely(!rl2)) {					err = -ENOMEM;					ntfs_error(vol->sb, "Failed to allocate memory.");					goto out;				}				memcpy(rl2, rl, rlsize);				kvfree(rl);				rl = rl2;				rlsize += PAGE_SIZE;				ntfs_debug("Reallocated memory, rlsize 0x%x.",						rlsize);			}			/* Allocate the bitmap bit. */			*byte |= bit;			/* We need to write this bitmap page to disk. */			need_writeback = 1;			ntfs_debug("*byte 0x%x, need_writeback is set.",					(unsigned int)*byte);			ntfs_dec_free_clusters(vol, 1);			ntfs_set_lcn_empty_bits(vol, index, 1, 1); 			/*			 * Coalesce with previous run if adjacent LCNs.			 * Otherwise, append a new run.			 */			ntfs_debug("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.",					lcn + bmp_pos, 1ULL, prev_lcn,					lcn, bmp_pos, prev_run_len, rlpos);			if (prev_lcn == lcn + bmp_pos - prev_run_len && rlpos) {				ntfs_debug("Coalescing to run (lcn 0x%llx, len 0x%llx).",						rl[rlpos - 1].lcn,						rl[rlpos - 1].length);				rl[rlpos - 1].length = ++prev_run_len;				ntfs_debug("Run now (lcn 0x%llx, len 0x%llx), prev_run_len 0x%llx.",						rl[rlpos - 1].lcn,						rl[rlpos - 1].length,						prev_run_len);			} else {				if (likely(rlpos)) {					ntfs_debug("Adding new run, (previous run lcn 0x%llx, len 0x%llx).",							rl[rlpos - 1].lcn, rl[rlpos - 1].length);					rl[rlpos].vcn = rl[rlpos - 1].vcn +							prev_run_len;				} else {					ntfs_debug("Adding new run, is first run.");					rl[rlpos].vcn = start_vcn;				}				rl[rlpos].lcn = prev_lcn = lcn + bmp_pos;				rl[rlpos].length = prev_run_len = 1;				rlpos++;			}			/* Done? */			if (!--clusters) {				s64 tc;done:				if (!used_zone_pos)					goto out;				/*				 * Update the current zone position.  Positions				 * of already scanned zones have been updated				 * during the respective zone switches.				 */				tc = lcn + bmp_pos + 1;				ntfs_debug("Done. Updating current zone position, tc 0x%llx, search_zone %i.",						tc, search_zone);				switch (search_zone) {				case 1:					ntfs_debug("Before checks, vol->mft_zone_pos 0x%llx.",							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_debug("After checks, vol->mft_zone_pos 0x%llx.",							vol->mft_zone_pos);					break;				case 2:					ntfs_debug("Before checks, vol->data1_zone_pos 0x%llx.",							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_debug("After checks, vol->data1_zone_pos 0x%llx.",							vol->data1_zone_pos);					break;				case 4:					ntfs_debug("Before checks, vol->data2_zone_pos 0x%llx.",							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_debug("After checks, vol->data2_zone_pos 0x%llx.",							vol->data2_zone_pos);					break;				default:					WARN_ON(1);

Lines 1–500 of 1055.