162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * bitmap.c - NTFS kernel bitmap handling. Part of the Linux-NTFS project. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2004-2005 Anton Altaparmakov 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#ifdef NTFS_RW 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/pagemap.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "bitmap.h" 1362306a36Sopenharmony_ci#include "debug.h" 1462306a36Sopenharmony_ci#include "aops.h" 1562306a36Sopenharmony_ci#include "ntfs.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/** 1862306a36Sopenharmony_ci * __ntfs_bitmap_set_bits_in_run - set a run of bits in a bitmap to a value 1962306a36Sopenharmony_ci * @vi: vfs inode describing the bitmap 2062306a36Sopenharmony_ci * @start_bit: first bit to set 2162306a36Sopenharmony_ci * @count: number of bits to set 2262306a36Sopenharmony_ci * @value: value to set the bits to (i.e. 0 or 1) 2362306a36Sopenharmony_ci * @is_rollback: if 'true' this is a rollback operation 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * Set @count bits starting at bit @start_bit in the bitmap described by the 2662306a36Sopenharmony_ci * vfs inode @vi to @value, where @value is either 0 or 1. 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * @is_rollback should always be 'false', it is for internal use to rollback 2962306a36Sopenharmony_ci * errors. You probably want to use ntfs_bitmap_set_bits_in_run() instead. 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * Return 0 on success and -errno on error. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ciint __ntfs_bitmap_set_bits_in_run(struct inode *vi, const s64 start_bit, 3462306a36Sopenharmony_ci const s64 count, const u8 value, const bool is_rollback) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci s64 cnt = count; 3762306a36Sopenharmony_ci pgoff_t index, end_index; 3862306a36Sopenharmony_ci struct address_space *mapping; 3962306a36Sopenharmony_ci struct page *page; 4062306a36Sopenharmony_ci u8 *kaddr; 4162306a36Sopenharmony_ci int pos, len; 4262306a36Sopenharmony_ci u8 bit; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci BUG_ON(!vi); 4562306a36Sopenharmony_ci ntfs_debug("Entering for i_ino 0x%lx, start_bit 0x%llx, count 0x%llx, " 4662306a36Sopenharmony_ci "value %u.%s", vi->i_ino, (unsigned long long)start_bit, 4762306a36Sopenharmony_ci (unsigned long long)cnt, (unsigned int)value, 4862306a36Sopenharmony_ci is_rollback ? " (rollback)" : ""); 4962306a36Sopenharmony_ci BUG_ON(start_bit < 0); 5062306a36Sopenharmony_ci BUG_ON(cnt < 0); 5162306a36Sopenharmony_ci BUG_ON(value > 1); 5262306a36Sopenharmony_ci /* 5362306a36Sopenharmony_ci * Calculate the indices for the pages containing the first and last 5462306a36Sopenharmony_ci * bits, i.e. @start_bit and @start_bit + @cnt - 1, respectively. 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_ci index = start_bit >> (3 + PAGE_SHIFT); 5762306a36Sopenharmony_ci end_index = (start_bit + cnt - 1) >> (3 + PAGE_SHIFT); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci /* Get the page containing the first bit (@start_bit). */ 6062306a36Sopenharmony_ci mapping = vi->i_mapping; 6162306a36Sopenharmony_ci page = ntfs_map_page(mapping, index); 6262306a36Sopenharmony_ci if (IS_ERR(page)) { 6362306a36Sopenharmony_ci if (!is_rollback) 6462306a36Sopenharmony_ci ntfs_error(vi->i_sb, "Failed to map first page (error " 6562306a36Sopenharmony_ci "%li), aborting.", PTR_ERR(page)); 6662306a36Sopenharmony_ci return PTR_ERR(page); 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci kaddr = page_address(page); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* Set @pos to the position of the byte containing @start_bit. */ 7162306a36Sopenharmony_ci pos = (start_bit >> 3) & ~PAGE_MASK; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* Calculate the position of @start_bit in the first byte. */ 7462306a36Sopenharmony_ci bit = start_bit & 7; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* If the first byte is partial, modify the appropriate bits in it. */ 7762306a36Sopenharmony_ci if (bit) { 7862306a36Sopenharmony_ci u8 *byte = kaddr + pos; 7962306a36Sopenharmony_ci while ((bit & 7) && cnt) { 8062306a36Sopenharmony_ci cnt--; 8162306a36Sopenharmony_ci if (value) 8262306a36Sopenharmony_ci *byte |= 1 << bit++; 8362306a36Sopenharmony_ci else 8462306a36Sopenharmony_ci *byte &= ~(1 << bit++); 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci /* If we are done, unmap the page and return success. */ 8762306a36Sopenharmony_ci if (!cnt) 8862306a36Sopenharmony_ci goto done; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* Update @pos to the new position. */ 9162306a36Sopenharmony_ci pos++; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci /* 9462306a36Sopenharmony_ci * Depending on @value, modify all remaining whole bytes in the page up 9562306a36Sopenharmony_ci * to @cnt. 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_ci len = min_t(s64, cnt >> 3, PAGE_SIZE - pos); 9862306a36Sopenharmony_ci memset(kaddr + pos, value ? 0xff : 0, len); 9962306a36Sopenharmony_ci cnt -= len << 3; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* Update @len to point to the first not-done byte in the page. */ 10262306a36Sopenharmony_ci if (cnt < 8) 10362306a36Sopenharmony_ci len += pos; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* If we are not in the last page, deal with all subsequent pages. */ 10662306a36Sopenharmony_ci while (index < end_index) { 10762306a36Sopenharmony_ci BUG_ON(cnt <= 0); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* Update @index and get the next page. */ 11062306a36Sopenharmony_ci flush_dcache_page(page); 11162306a36Sopenharmony_ci set_page_dirty(page); 11262306a36Sopenharmony_ci ntfs_unmap_page(page); 11362306a36Sopenharmony_ci page = ntfs_map_page(mapping, ++index); 11462306a36Sopenharmony_ci if (IS_ERR(page)) 11562306a36Sopenharmony_ci goto rollback; 11662306a36Sopenharmony_ci kaddr = page_address(page); 11762306a36Sopenharmony_ci /* 11862306a36Sopenharmony_ci * Depending on @value, modify all remaining whole bytes in the 11962306a36Sopenharmony_ci * page up to @cnt. 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_ci len = min_t(s64, cnt >> 3, PAGE_SIZE); 12262306a36Sopenharmony_ci memset(kaddr, value ? 0xff : 0, len); 12362306a36Sopenharmony_ci cnt -= len << 3; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci /* 12662306a36Sopenharmony_ci * The currently mapped page is the last one. If the last byte is 12762306a36Sopenharmony_ci * partial, modify the appropriate bits in it. Note, @len is the 12862306a36Sopenharmony_ci * position of the last byte inside the page. 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_ci if (cnt) { 13162306a36Sopenharmony_ci u8 *byte; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci BUG_ON(cnt > 7); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci bit = cnt; 13662306a36Sopenharmony_ci byte = kaddr + len; 13762306a36Sopenharmony_ci while (bit--) { 13862306a36Sopenharmony_ci if (value) 13962306a36Sopenharmony_ci *byte |= 1 << bit; 14062306a36Sopenharmony_ci else 14162306a36Sopenharmony_ci *byte &= ~(1 << bit); 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_cidone: 14562306a36Sopenharmony_ci /* We are done. Unmap the page and return success. */ 14662306a36Sopenharmony_ci flush_dcache_page(page); 14762306a36Sopenharmony_ci set_page_dirty(page); 14862306a36Sopenharmony_ci ntfs_unmap_page(page); 14962306a36Sopenharmony_ci ntfs_debug("Done."); 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_cirollback: 15262306a36Sopenharmony_ci /* 15362306a36Sopenharmony_ci * Current state: 15462306a36Sopenharmony_ci * - no pages are mapped 15562306a36Sopenharmony_ci * - @count - @cnt is the number of bits that have been modified 15662306a36Sopenharmony_ci */ 15762306a36Sopenharmony_ci if (is_rollback) 15862306a36Sopenharmony_ci return PTR_ERR(page); 15962306a36Sopenharmony_ci if (count != cnt) 16062306a36Sopenharmony_ci pos = __ntfs_bitmap_set_bits_in_run(vi, start_bit, count - cnt, 16162306a36Sopenharmony_ci value ? 0 : 1, true); 16262306a36Sopenharmony_ci else 16362306a36Sopenharmony_ci pos = 0; 16462306a36Sopenharmony_ci if (!pos) { 16562306a36Sopenharmony_ci /* Rollback was successful. */ 16662306a36Sopenharmony_ci ntfs_error(vi->i_sb, "Failed to map subsequent page (error " 16762306a36Sopenharmony_ci "%li), aborting.", PTR_ERR(page)); 16862306a36Sopenharmony_ci } else { 16962306a36Sopenharmony_ci /* Rollback failed. */ 17062306a36Sopenharmony_ci ntfs_error(vi->i_sb, "Failed to map subsequent page (error " 17162306a36Sopenharmony_ci "%li) and rollback failed (error %i). " 17262306a36Sopenharmony_ci "Aborting and leaving inconsistent metadata. " 17362306a36Sopenharmony_ci "Unmount and run chkdsk.", PTR_ERR(page), pos); 17462306a36Sopenharmony_ci NVolSetErrors(NTFS_SB(vi->i_sb)); 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci return PTR_ERR(page); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci#endif /* NTFS_RW */ 180