18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * bitmap.c - NTFS kernel bitmap handling. Part of the Linux-NTFS project. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2004-2005 Anton Altaparmakov 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#ifdef NTFS_RW 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "bitmap.h" 138c2ecf20Sopenharmony_ci#include "debug.h" 148c2ecf20Sopenharmony_ci#include "aops.h" 158c2ecf20Sopenharmony_ci#include "ntfs.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/** 188c2ecf20Sopenharmony_ci * __ntfs_bitmap_set_bits_in_run - set a run of bits in a bitmap to a value 198c2ecf20Sopenharmony_ci * @vi: vfs inode describing the bitmap 208c2ecf20Sopenharmony_ci * @start_bit: first bit to set 218c2ecf20Sopenharmony_ci * @count: number of bits to set 228c2ecf20Sopenharmony_ci * @value: value to set the bits to (i.e. 0 or 1) 238c2ecf20Sopenharmony_ci * @is_rollback: if 'true' this is a rollback operation 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * Set @count bits starting at bit @start_bit in the bitmap described by the 268c2ecf20Sopenharmony_ci * vfs inode @vi to @value, where @value is either 0 or 1. 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * @is_rollback should always be 'false', it is for internal use to rollback 298c2ecf20Sopenharmony_ci * errors. You probably want to use ntfs_bitmap_set_bits_in_run() instead. 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * Return 0 on success and -errno on error. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ciint __ntfs_bitmap_set_bits_in_run(struct inode *vi, const s64 start_bit, 348c2ecf20Sopenharmony_ci const s64 count, const u8 value, const bool is_rollback) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci s64 cnt = count; 378c2ecf20Sopenharmony_ci pgoff_t index, end_index; 388c2ecf20Sopenharmony_ci struct address_space *mapping; 398c2ecf20Sopenharmony_ci struct page *page; 408c2ecf20Sopenharmony_ci u8 *kaddr; 418c2ecf20Sopenharmony_ci int pos, len; 428c2ecf20Sopenharmony_ci u8 bit; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci BUG_ON(!vi); 458c2ecf20Sopenharmony_ci ntfs_debug("Entering for i_ino 0x%lx, start_bit 0x%llx, count 0x%llx, " 468c2ecf20Sopenharmony_ci "value %u.%s", vi->i_ino, (unsigned long long)start_bit, 478c2ecf20Sopenharmony_ci (unsigned long long)cnt, (unsigned int)value, 488c2ecf20Sopenharmony_ci is_rollback ? " (rollback)" : ""); 498c2ecf20Sopenharmony_ci BUG_ON(start_bit < 0); 508c2ecf20Sopenharmony_ci BUG_ON(cnt < 0); 518c2ecf20Sopenharmony_ci BUG_ON(value > 1); 528c2ecf20Sopenharmony_ci /* 538c2ecf20Sopenharmony_ci * Calculate the indices for the pages containing the first and last 548c2ecf20Sopenharmony_ci * bits, i.e. @start_bit and @start_bit + @cnt - 1, respectively. 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ci index = start_bit >> (3 + PAGE_SHIFT); 578c2ecf20Sopenharmony_ci end_index = (start_bit + cnt - 1) >> (3 + PAGE_SHIFT); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci /* Get the page containing the first bit (@start_bit). */ 608c2ecf20Sopenharmony_ci mapping = vi->i_mapping; 618c2ecf20Sopenharmony_ci page = ntfs_map_page(mapping, index); 628c2ecf20Sopenharmony_ci if (IS_ERR(page)) { 638c2ecf20Sopenharmony_ci if (!is_rollback) 648c2ecf20Sopenharmony_ci ntfs_error(vi->i_sb, "Failed to map first page (error " 658c2ecf20Sopenharmony_ci "%li), aborting.", PTR_ERR(page)); 668c2ecf20Sopenharmony_ci return PTR_ERR(page); 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci kaddr = page_address(page); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* Set @pos to the position of the byte containing @start_bit. */ 718c2ecf20Sopenharmony_ci pos = (start_bit >> 3) & ~PAGE_MASK; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* Calculate the position of @start_bit in the first byte. */ 748c2ecf20Sopenharmony_ci bit = start_bit & 7; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci /* If the first byte is partial, modify the appropriate bits in it. */ 778c2ecf20Sopenharmony_ci if (bit) { 788c2ecf20Sopenharmony_ci u8 *byte = kaddr + pos; 798c2ecf20Sopenharmony_ci while ((bit & 7) && cnt) { 808c2ecf20Sopenharmony_ci cnt--; 818c2ecf20Sopenharmony_ci if (value) 828c2ecf20Sopenharmony_ci *byte |= 1 << bit++; 838c2ecf20Sopenharmony_ci else 848c2ecf20Sopenharmony_ci *byte &= ~(1 << bit++); 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci /* If we are done, unmap the page and return success. */ 878c2ecf20Sopenharmony_ci if (!cnt) 888c2ecf20Sopenharmony_ci goto done; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* Update @pos to the new position. */ 918c2ecf20Sopenharmony_ci pos++; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci /* 948c2ecf20Sopenharmony_ci * Depending on @value, modify all remaining whole bytes in the page up 958c2ecf20Sopenharmony_ci * to @cnt. 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci len = min_t(s64, cnt >> 3, PAGE_SIZE - pos); 988c2ecf20Sopenharmony_ci memset(kaddr + pos, value ? 0xff : 0, len); 998c2ecf20Sopenharmony_ci cnt -= len << 3; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* Update @len to point to the first not-done byte in the page. */ 1028c2ecf20Sopenharmony_ci if (cnt < 8) 1038c2ecf20Sopenharmony_ci len += pos; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* If we are not in the last page, deal with all subsequent pages. */ 1068c2ecf20Sopenharmony_ci while (index < end_index) { 1078c2ecf20Sopenharmony_ci BUG_ON(cnt <= 0); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* Update @index and get the next page. */ 1108c2ecf20Sopenharmony_ci flush_dcache_page(page); 1118c2ecf20Sopenharmony_ci set_page_dirty(page); 1128c2ecf20Sopenharmony_ci ntfs_unmap_page(page); 1138c2ecf20Sopenharmony_ci page = ntfs_map_page(mapping, ++index); 1148c2ecf20Sopenharmony_ci if (IS_ERR(page)) 1158c2ecf20Sopenharmony_ci goto rollback; 1168c2ecf20Sopenharmony_ci kaddr = page_address(page); 1178c2ecf20Sopenharmony_ci /* 1188c2ecf20Sopenharmony_ci * Depending on @value, modify all remaining whole bytes in the 1198c2ecf20Sopenharmony_ci * page up to @cnt. 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_ci len = min_t(s64, cnt >> 3, PAGE_SIZE); 1228c2ecf20Sopenharmony_ci memset(kaddr, value ? 0xff : 0, len); 1238c2ecf20Sopenharmony_ci cnt -= len << 3; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci /* 1268c2ecf20Sopenharmony_ci * The currently mapped page is the last one. If the last byte is 1278c2ecf20Sopenharmony_ci * partial, modify the appropriate bits in it. Note, @len is the 1288c2ecf20Sopenharmony_ci * position of the last byte inside the page. 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_ci if (cnt) { 1318c2ecf20Sopenharmony_ci u8 *byte; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci BUG_ON(cnt > 7); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci bit = cnt; 1368c2ecf20Sopenharmony_ci byte = kaddr + len; 1378c2ecf20Sopenharmony_ci while (bit--) { 1388c2ecf20Sopenharmony_ci if (value) 1398c2ecf20Sopenharmony_ci *byte |= 1 << bit; 1408c2ecf20Sopenharmony_ci else 1418c2ecf20Sopenharmony_ci *byte &= ~(1 << bit); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_cidone: 1458c2ecf20Sopenharmony_ci /* We are done. Unmap the page and return success. */ 1468c2ecf20Sopenharmony_ci flush_dcache_page(page); 1478c2ecf20Sopenharmony_ci set_page_dirty(page); 1488c2ecf20Sopenharmony_ci ntfs_unmap_page(page); 1498c2ecf20Sopenharmony_ci ntfs_debug("Done."); 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_cirollback: 1528c2ecf20Sopenharmony_ci /* 1538c2ecf20Sopenharmony_ci * Current state: 1548c2ecf20Sopenharmony_ci * - no pages are mapped 1558c2ecf20Sopenharmony_ci * - @count - @cnt is the number of bits that have been modified 1568c2ecf20Sopenharmony_ci */ 1578c2ecf20Sopenharmony_ci if (is_rollback) 1588c2ecf20Sopenharmony_ci return PTR_ERR(page); 1598c2ecf20Sopenharmony_ci if (count != cnt) 1608c2ecf20Sopenharmony_ci pos = __ntfs_bitmap_set_bits_in_run(vi, start_bit, count - cnt, 1618c2ecf20Sopenharmony_ci value ? 0 : 1, true); 1628c2ecf20Sopenharmony_ci else 1638c2ecf20Sopenharmony_ci pos = 0; 1648c2ecf20Sopenharmony_ci if (!pos) { 1658c2ecf20Sopenharmony_ci /* Rollback was successful. */ 1668c2ecf20Sopenharmony_ci ntfs_error(vi->i_sb, "Failed to map subsequent page (error " 1678c2ecf20Sopenharmony_ci "%li), aborting.", PTR_ERR(page)); 1688c2ecf20Sopenharmony_ci } else { 1698c2ecf20Sopenharmony_ci /* Rollback failed. */ 1708c2ecf20Sopenharmony_ci ntfs_error(vi->i_sb, "Failed to map subsequent page (error " 1718c2ecf20Sopenharmony_ci "%li) and rollback failed (error %i). " 1728c2ecf20Sopenharmony_ci "Aborting and leaving inconsistent metadata. " 1738c2ecf20Sopenharmony_ci "Unmount and run chkdsk.", PTR_ERR(page), pos); 1748c2ecf20Sopenharmony_ci NVolSetErrors(NTFS_SB(vi->i_sb)); 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci return PTR_ERR(page); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci#endif /* NTFS_RW */ 180