xref: /linux/fs/ntfs/bitmap.c (revision cdd4dc3aebeab43a72ce0bc2b5bab6f0a80b97a5)
11e9ea7e0SNamjae Jeon // SPDX-License-Identifier: GPL-2.0-or-later
21e9ea7e0SNamjae Jeon /*
311ccc910SNamjae Jeon  * NTFS kernel bitmap handling.
41e9ea7e0SNamjae Jeon  *
51e9ea7e0SNamjae Jeon  * Copyright (c) 2004-2005 Anton Altaparmakov
611ccc910SNamjae Jeon  * Copyright (c) 2025 LG Electronics Co., Ltd.
71e9ea7e0SNamjae Jeon  */
81e9ea7e0SNamjae Jeon 
911ccc910SNamjae Jeon #include <linux/bitops.h>
1011ccc910SNamjae Jeon #include <linux/blkdev.h>
111e9ea7e0SNamjae Jeon 
121e9ea7e0SNamjae Jeon #include "bitmap.h"
131e9ea7e0SNamjae Jeon #include "ntfs.h"
141e9ea7e0SNamjae Jeon 
1511ccc910SNamjae Jeon int ntfs_trim_fs(struct ntfs_volume *vol, struct fstrim_range *range)
1611ccc910SNamjae Jeon {
1711ccc910SNamjae Jeon 	size_t buf_clusters;
1811ccc910SNamjae Jeon 	pgoff_t index, start_index, end_index;
1911ccc910SNamjae Jeon 	struct file_ra_state *ra;
2011ccc910SNamjae Jeon 	struct folio *folio;
2111ccc910SNamjae Jeon 	unsigned long *bitmap;
2211ccc910SNamjae Jeon 	char *kaddr;
2311ccc910SNamjae Jeon 	u64 end, trimmed = 0, start_buf, end_buf, end_cluster;
2411ccc910SNamjae Jeon 	u64 start_cluster = ntfs_bytes_to_cluster(vol, range->start);
2511ccc910SNamjae Jeon 	u32 dq = bdev_discard_granularity(vol->sb->s_bdev);
2611ccc910SNamjae Jeon 	int ret = 0;
2711ccc910SNamjae Jeon 
2811ccc910SNamjae Jeon 	if (!dq)
2911ccc910SNamjae Jeon 		dq = vol->cluster_size;
3011ccc910SNamjae Jeon 
3111ccc910SNamjae Jeon 	if (start_cluster >= vol->nr_clusters)
3211ccc910SNamjae Jeon 		return -EINVAL;
3311ccc910SNamjae Jeon 
3411ccc910SNamjae Jeon 	if (range->len == (u64)-1)
3511ccc910SNamjae Jeon 		end_cluster = vol->nr_clusters;
3611ccc910SNamjae Jeon 	else {
3711ccc910SNamjae Jeon 		end_cluster = ntfs_bytes_to_cluster(vol,
3811ccc910SNamjae Jeon 				(range->start + range->len + vol->cluster_size - 1));
3911ccc910SNamjae Jeon 		if (end_cluster > vol->nr_clusters)
4011ccc910SNamjae Jeon 			end_cluster = vol->nr_clusters;
4111ccc910SNamjae Jeon 	}
4211ccc910SNamjae Jeon 
4311ccc910SNamjae Jeon 	ra = kzalloc(sizeof(*ra), GFP_NOFS);
4411ccc910SNamjae Jeon 	if (!ra)
4511ccc910SNamjae Jeon 		return -ENOMEM;
4611ccc910SNamjae Jeon 
4711ccc910SNamjae Jeon 	buf_clusters = PAGE_SIZE * 8;
4811ccc910SNamjae Jeon 	start_index = start_cluster >> 15;
4911ccc910SNamjae Jeon 	end_index = (end_cluster + buf_clusters - 1) >> 15;
5011ccc910SNamjae Jeon 
5111ccc910SNamjae Jeon 	for (index = start_index; index < end_index; index++) {
5211ccc910SNamjae Jeon 		folio = ntfs_get_locked_folio(vol->lcnbmp_ino->i_mapping,
5311ccc910SNamjae Jeon 				index, end_index, ra);
5411ccc910SNamjae Jeon 		if (IS_ERR(folio)) {
5511ccc910SNamjae Jeon 			ret = PTR_ERR(folio);
5611ccc910SNamjae Jeon 			goto out_free;
5711ccc910SNamjae Jeon 		}
5811ccc910SNamjae Jeon 
5911ccc910SNamjae Jeon 		kaddr = kmap_local_folio(folio, 0);
6011ccc910SNamjae Jeon 		bitmap = (unsigned long *)kaddr;
6111ccc910SNamjae Jeon 
6211ccc910SNamjae Jeon 		start_buf = max_t(u64, index * buf_clusters, start_cluster);
6311ccc910SNamjae Jeon 		end_buf = min_t(u64, (index + 1) * buf_clusters, end_cluster);
6411ccc910SNamjae Jeon 
6511ccc910SNamjae Jeon 		end = start_buf;
6611ccc910SNamjae Jeon 		while (end < end_buf) {
6711ccc910SNamjae Jeon 			u64 aligned_start, aligned_count;
6811ccc910SNamjae Jeon 			u64 start = find_next_zero_bit(bitmap, end_buf - start_buf,
6911ccc910SNamjae Jeon 					end - start_buf) + start_buf;
7011ccc910SNamjae Jeon 			if (start >= end_buf)
7111ccc910SNamjae Jeon 				break;
7211ccc910SNamjae Jeon 
7311ccc910SNamjae Jeon 			end = find_next_bit(bitmap, end_buf - start_buf,
7411ccc910SNamjae Jeon 					start - start_buf) + start_buf;
7511ccc910SNamjae Jeon 
7611ccc910SNamjae Jeon 			aligned_start = ALIGN(ntfs_cluster_to_bytes(vol, start), dq);
7711ccc910SNamjae Jeon 			aligned_count =
7811ccc910SNamjae Jeon 				ALIGN_DOWN(ntfs_cluster_to_bytes(vol, end - start), dq);
7911ccc910SNamjae Jeon 			if (aligned_count >= range->minlen) {
8011ccc910SNamjae Jeon 				ret = blkdev_issue_discard(vol->sb->s_bdev, aligned_start >> 9,
8111ccc910SNamjae Jeon 						aligned_count >> 9, GFP_NOFS);
8211ccc910SNamjae Jeon 				if (ret)
8311ccc910SNamjae Jeon 					goto out_unmap;
8411ccc910SNamjae Jeon 				trimmed += aligned_count;
8511ccc910SNamjae Jeon 			}
8611ccc910SNamjae Jeon 		}
8711ccc910SNamjae Jeon 
8811ccc910SNamjae Jeon out_unmap:
8911ccc910SNamjae Jeon 		kunmap_local(kaddr);
9011ccc910SNamjae Jeon 		folio_unlock(folio);
9111ccc910SNamjae Jeon 		folio_put(folio);
9211ccc910SNamjae Jeon 
9311ccc910SNamjae Jeon 		if (ret)
9411ccc910SNamjae Jeon 			goto out_free;
9511ccc910SNamjae Jeon 	}
9611ccc910SNamjae Jeon 
9711ccc910SNamjae Jeon 	range->len = trimmed;
9811ccc910SNamjae Jeon 
9911ccc910SNamjae Jeon out_free:
10011ccc910SNamjae Jeon 	kfree(ra);
10111ccc910SNamjae Jeon 	return ret;
10211ccc910SNamjae Jeon }
10311ccc910SNamjae Jeon 
10411ccc910SNamjae Jeon /*
1051e9ea7e0SNamjae Jeon  * __ntfs_bitmap_set_bits_in_run - set a run of bits in a bitmap to a value
1061e9ea7e0SNamjae Jeon  * @vi:			vfs inode describing the bitmap
1071e9ea7e0SNamjae Jeon  * @start_bit:		first bit to set
1081e9ea7e0SNamjae Jeon  * @count:		number of bits to set
1091e9ea7e0SNamjae Jeon  * @value:		value to set the bits to (i.e. 0 or 1)
1101e9ea7e0SNamjae Jeon  * @is_rollback:	if 'true' this is a rollback operation
1111e9ea7e0SNamjae Jeon  *
1121e9ea7e0SNamjae Jeon  * Set @count bits starting at bit @start_bit in the bitmap described by the
1131e9ea7e0SNamjae Jeon  * vfs inode @vi to @value, where @value is either 0 or 1.
1141e9ea7e0SNamjae Jeon  *
1151e9ea7e0SNamjae Jeon  * @is_rollback should always be 'false', it is for internal use to rollback
1161e9ea7e0SNamjae Jeon  * errors.  You probably want to use ntfs_bitmap_set_bits_in_run() instead.
1171e9ea7e0SNamjae Jeon  *
1181e9ea7e0SNamjae Jeon  * Return 0 on success and -errno on error.
1191e9ea7e0SNamjae Jeon  */
1201e9ea7e0SNamjae Jeon int __ntfs_bitmap_set_bits_in_run(struct inode *vi, const s64 start_bit,
1211e9ea7e0SNamjae Jeon 		const s64 count, const u8 value, const bool is_rollback)
1221e9ea7e0SNamjae Jeon {
1231e9ea7e0SNamjae Jeon 	s64 cnt = count;
1241e9ea7e0SNamjae Jeon 	pgoff_t index, end_index;
1251e9ea7e0SNamjae Jeon 	struct address_space *mapping;
12611ccc910SNamjae Jeon 	struct folio *folio;
1271e9ea7e0SNamjae Jeon 	u8 *kaddr;
1281e9ea7e0SNamjae Jeon 	int pos, len;
1291e9ea7e0SNamjae Jeon 	u8 bit;
13011ccc910SNamjae Jeon 	struct ntfs_inode *ni = NTFS_I(vi);
13111ccc910SNamjae Jeon 	struct ntfs_volume *vol = ni->vol;
1321e9ea7e0SNamjae Jeon 
133*e7d82353SNamjae Jeon 	ntfs_debug("Entering for i_ino 0x%llx, start_bit 0x%llx, count 0x%llx, value %u.%s",
134*e7d82353SNamjae Jeon 			ni->mft_no, (unsigned long long)start_bit,
1351e9ea7e0SNamjae Jeon 			(unsigned long long)cnt, (unsigned int)value,
1361e9ea7e0SNamjae Jeon 			is_rollback ? " (rollback)" : "");
13711ccc910SNamjae Jeon 
13811ccc910SNamjae Jeon 	if (start_bit < 0 || cnt < 0 || value > 1)
13911ccc910SNamjae Jeon 		return -EINVAL;
14011ccc910SNamjae Jeon 
1411e9ea7e0SNamjae Jeon 	/*
1421e9ea7e0SNamjae Jeon 	 * Calculate the indices for the pages containing the first and last
1431e9ea7e0SNamjae Jeon 	 * bits, i.e. @start_bit and @start_bit + @cnt - 1, respectively.
1441e9ea7e0SNamjae Jeon 	 */
1451e9ea7e0SNamjae Jeon 	index = start_bit >> (3 + PAGE_SHIFT);
1461e9ea7e0SNamjae Jeon 	end_index = (start_bit + cnt - 1) >> (3 + PAGE_SHIFT);
1471e9ea7e0SNamjae Jeon 
1481e9ea7e0SNamjae Jeon 	/* Get the page containing the first bit (@start_bit). */
1491e9ea7e0SNamjae Jeon 	mapping = vi->i_mapping;
15011ccc910SNamjae Jeon 	folio = read_mapping_folio(mapping, index, NULL);
15111ccc910SNamjae Jeon 	if (IS_ERR(folio)) {
1521e9ea7e0SNamjae Jeon 		if (!is_rollback)
15311ccc910SNamjae Jeon 			ntfs_error(vi->i_sb,
15411ccc910SNamjae Jeon 				"Failed to map first page (error %li), aborting.",
15511ccc910SNamjae Jeon 				PTR_ERR(folio));
15611ccc910SNamjae Jeon 		return PTR_ERR(folio);
1571e9ea7e0SNamjae Jeon 	}
15811ccc910SNamjae Jeon 
15911ccc910SNamjae Jeon 	folio_lock(folio);
16011ccc910SNamjae Jeon 	kaddr = kmap_local_folio(folio, 0);
1611e9ea7e0SNamjae Jeon 
1621e9ea7e0SNamjae Jeon 	/* Set @pos to the position of the byte containing @start_bit. */
1631e9ea7e0SNamjae Jeon 	pos = (start_bit >> 3) & ~PAGE_MASK;
1641e9ea7e0SNamjae Jeon 
1651e9ea7e0SNamjae Jeon 	/* Calculate the position of @start_bit in the first byte. */
1661e9ea7e0SNamjae Jeon 	bit = start_bit & 7;
1671e9ea7e0SNamjae Jeon 
1681e9ea7e0SNamjae Jeon 	/* If the first byte is partial, modify the appropriate bits in it. */
1691e9ea7e0SNamjae Jeon 	if (bit) {
1701e9ea7e0SNamjae Jeon 		u8 *byte = kaddr + pos;
17111ccc910SNamjae Jeon 
17211ccc910SNamjae Jeon 		if (ni->mft_no == FILE_Bitmap)
17311ccc910SNamjae Jeon 			ntfs_set_lcn_empty_bits(vol, index, value, min_t(s64, 8 - bit, cnt));
1741e9ea7e0SNamjae Jeon 		while ((bit & 7) && cnt) {
1751e9ea7e0SNamjae Jeon 			cnt--;
1761e9ea7e0SNamjae Jeon 			if (value)
1771e9ea7e0SNamjae Jeon 				*byte |= 1 << bit++;
1781e9ea7e0SNamjae Jeon 			else
1791e9ea7e0SNamjae Jeon 				*byte &= ~(1 << bit++);
1801e9ea7e0SNamjae Jeon 		}
1811e9ea7e0SNamjae Jeon 		/* If we are done, unmap the page and return success. */
1821e9ea7e0SNamjae Jeon 		if (!cnt)
1831e9ea7e0SNamjae Jeon 			goto done;
1841e9ea7e0SNamjae Jeon 
1851e9ea7e0SNamjae Jeon 		/* Update @pos to the new position. */
1861e9ea7e0SNamjae Jeon 		pos++;
1871e9ea7e0SNamjae Jeon 	}
1881e9ea7e0SNamjae Jeon 	/*
1891e9ea7e0SNamjae Jeon 	 * Depending on @value, modify all remaining whole bytes in the page up
1901e9ea7e0SNamjae Jeon 	 * to @cnt.
1911e9ea7e0SNamjae Jeon 	 */
1921e9ea7e0SNamjae Jeon 	len = min_t(s64, cnt >> 3, PAGE_SIZE - pos);
1931e9ea7e0SNamjae Jeon 	memset(kaddr + pos, value ? 0xff : 0, len);
1941e9ea7e0SNamjae Jeon 	cnt -= len << 3;
19511ccc910SNamjae Jeon 	if (ni->mft_no == FILE_Bitmap)
19611ccc910SNamjae Jeon 		ntfs_set_lcn_empty_bits(vol, index, value, len << 3);
1971e9ea7e0SNamjae Jeon 
1981e9ea7e0SNamjae Jeon 	/* Update @len to point to the first not-done byte in the page. */
1991e9ea7e0SNamjae Jeon 	if (cnt < 8)
2001e9ea7e0SNamjae Jeon 		len += pos;
2011e9ea7e0SNamjae Jeon 
2021e9ea7e0SNamjae Jeon 	/* If we are not in the last page, deal with all subsequent pages. */
2031e9ea7e0SNamjae Jeon 	while (index < end_index) {
20411ccc910SNamjae Jeon 		if (cnt <= 0)
2051e9ea7e0SNamjae Jeon 			goto rollback;
20611ccc910SNamjae Jeon 
20711ccc910SNamjae Jeon 		/* Update @index and get the next folio. */
20811ccc910SNamjae Jeon 		folio_mark_dirty(folio);
20911ccc910SNamjae Jeon 		folio_unlock(folio);
21011ccc910SNamjae Jeon 		kunmap_local(kaddr);
21111ccc910SNamjae Jeon 		folio_put(folio);
21211ccc910SNamjae Jeon 		folio = read_mapping_folio(mapping, ++index, NULL);
21311ccc910SNamjae Jeon 		if (IS_ERR(folio)) {
21411ccc910SNamjae Jeon 			ntfs_error(vi->i_sb,
21511ccc910SNamjae Jeon 				   "Failed to map subsequent page (error %li), aborting.",
21611ccc910SNamjae Jeon 				   PTR_ERR(folio));
21711ccc910SNamjae Jeon 			goto rollback;
21811ccc910SNamjae Jeon 		}
21911ccc910SNamjae Jeon 
22011ccc910SNamjae Jeon 		folio_lock(folio);
22111ccc910SNamjae Jeon 		kaddr = kmap_local_folio(folio, 0);
2221e9ea7e0SNamjae Jeon 		/*
2231e9ea7e0SNamjae Jeon 		 * Depending on @value, modify all remaining whole bytes in the
2241e9ea7e0SNamjae Jeon 		 * page up to @cnt.
2251e9ea7e0SNamjae Jeon 		 */
2261e9ea7e0SNamjae Jeon 		len = min_t(s64, cnt >> 3, PAGE_SIZE);
2271e9ea7e0SNamjae Jeon 		memset(kaddr, value ? 0xff : 0, len);
2281e9ea7e0SNamjae Jeon 		cnt -= len << 3;
22911ccc910SNamjae Jeon 		if (ni->mft_no == FILE_Bitmap)
23011ccc910SNamjae Jeon 			ntfs_set_lcn_empty_bits(vol, index, value, len << 3);
2311e9ea7e0SNamjae Jeon 	}
2321e9ea7e0SNamjae Jeon 	/*
2331e9ea7e0SNamjae Jeon 	 * The currently mapped page is the last one.  If the last byte is
2341e9ea7e0SNamjae Jeon 	 * partial, modify the appropriate bits in it.  Note, @len is the
2351e9ea7e0SNamjae Jeon 	 * position of the last byte inside the page.
2361e9ea7e0SNamjae Jeon 	 */
2371e9ea7e0SNamjae Jeon 	if (cnt) {
2381e9ea7e0SNamjae Jeon 		u8 *byte;
2391e9ea7e0SNamjae Jeon 
24011ccc910SNamjae Jeon 		WARN_ON(cnt > 7);
2411e9ea7e0SNamjae Jeon 
2421e9ea7e0SNamjae Jeon 		bit = cnt;
2431e9ea7e0SNamjae Jeon 		byte = kaddr + len;
24411ccc910SNamjae Jeon 		if (ni->mft_no == FILE_Bitmap)
24511ccc910SNamjae Jeon 			ntfs_set_lcn_empty_bits(vol, index, value, bit);
2461e9ea7e0SNamjae Jeon 		while (bit--) {
2471e9ea7e0SNamjae Jeon 			if (value)
2481e9ea7e0SNamjae Jeon 				*byte |= 1 << bit;
2491e9ea7e0SNamjae Jeon 			else
2501e9ea7e0SNamjae Jeon 				*byte &= ~(1 << bit);
2511e9ea7e0SNamjae Jeon 		}
2521e9ea7e0SNamjae Jeon 	}
2531e9ea7e0SNamjae Jeon done:
25411ccc910SNamjae Jeon 	/* We are done.  Unmap the folio and return success. */
25511ccc910SNamjae Jeon 	folio_mark_dirty(folio);
25611ccc910SNamjae Jeon 	folio_unlock(folio);
25711ccc910SNamjae Jeon 	kunmap_local(kaddr);
25811ccc910SNamjae Jeon 	folio_put(folio);
2591e9ea7e0SNamjae Jeon 	ntfs_debug("Done.");
2601e9ea7e0SNamjae Jeon 	return 0;
2611e9ea7e0SNamjae Jeon rollback:
2621e9ea7e0SNamjae Jeon 	/*
2631e9ea7e0SNamjae Jeon 	 * Current state:
2641e9ea7e0SNamjae Jeon 	 *	- no pages are mapped
2651e9ea7e0SNamjae Jeon 	 *	- @count - @cnt is the number of bits that have been modified
2661e9ea7e0SNamjae Jeon 	 */
2671e9ea7e0SNamjae Jeon 	if (is_rollback)
26811ccc910SNamjae Jeon 		return PTR_ERR(folio);
2691e9ea7e0SNamjae Jeon 	if (count != cnt)
2701e9ea7e0SNamjae Jeon 		pos = __ntfs_bitmap_set_bits_in_run(vi, start_bit, count - cnt,
2711e9ea7e0SNamjae Jeon 				value ? 0 : 1, true);
2721e9ea7e0SNamjae Jeon 	else
2731e9ea7e0SNamjae Jeon 		pos = 0;
2741e9ea7e0SNamjae Jeon 	if (!pos) {
2751e9ea7e0SNamjae Jeon 		/* Rollback was successful. */
27611ccc910SNamjae Jeon 		ntfs_error(vi->i_sb,
27711ccc910SNamjae Jeon 			"Failed to map subsequent page (error %li), aborting.",
27811ccc910SNamjae Jeon 			PTR_ERR(folio));
2791e9ea7e0SNamjae Jeon 	} else {
2801e9ea7e0SNamjae Jeon 		/* Rollback failed. */
28111ccc910SNamjae Jeon 		ntfs_error(vi->i_sb,
28211ccc910SNamjae Jeon 			"Failed to map subsequent page (error %li) and rollback failed (error %i). Aborting and leaving inconsistent metadata. Unmount and run chkdsk.",
28311ccc910SNamjae Jeon 			PTR_ERR(folio), pos);
2841e9ea7e0SNamjae Jeon 		NVolSetErrors(NTFS_SB(vi->i_sb));
2851e9ea7e0SNamjae Jeon 	}
28611ccc910SNamjae Jeon 	return PTR_ERR(folio);
2871e9ea7e0SNamjae Jeon }
288