1*a1d312deSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * bitmap.c - NTFS kernel bitmap handling. Part of the Linux-NTFS project.
41da177e4SLinus Torvalds *
518efefa9SAnton Altaparmakov * Copyright (c) 2004-2005 Anton Altaparmakov
61da177e4SLinus Torvalds */
71da177e4SLinus Torvalds
81da177e4SLinus Torvalds #ifdef NTFS_RW
91da177e4SLinus Torvalds
101da177e4SLinus Torvalds #include <linux/pagemap.h>
111da177e4SLinus Torvalds
121da177e4SLinus Torvalds #include "bitmap.h"
131da177e4SLinus Torvalds #include "debug.h"
141da177e4SLinus Torvalds #include "aops.h"
151da177e4SLinus Torvalds #include "ntfs.h"
161da177e4SLinus Torvalds
171da177e4SLinus Torvalds /**
181da177e4SLinus Torvalds * __ntfs_bitmap_set_bits_in_run - set a run of bits in a bitmap to a value
191da177e4SLinus Torvalds * @vi: vfs inode describing the bitmap
201da177e4SLinus Torvalds * @start_bit: first bit to set
211da177e4SLinus Torvalds * @count: number of bits to set
221da177e4SLinus Torvalds * @value: value to set the bits to (i.e. 0 or 1)
23c49c3111SRichard Knutsson * @is_rollback: if 'true' this is a rollback operation
241da177e4SLinus Torvalds *
251da177e4SLinus Torvalds * Set @count bits starting at bit @start_bit in the bitmap described by the
261da177e4SLinus Torvalds * vfs inode @vi to @value, where @value is either 0 or 1.
271da177e4SLinus Torvalds *
28c49c3111SRichard Knutsson * @is_rollback should always be 'false', it is for internal use to rollback
291da177e4SLinus Torvalds * errors. You probably want to use ntfs_bitmap_set_bits_in_run() instead.
301da177e4SLinus Torvalds *
311da177e4SLinus Torvalds * Return 0 on success and -errno on error.
321da177e4SLinus Torvalds */
__ntfs_bitmap_set_bits_in_run(struct inode * vi,const s64 start_bit,const s64 count,const u8 value,const bool is_rollback)331da177e4SLinus Torvalds int __ntfs_bitmap_set_bits_in_run(struct inode *vi, const s64 start_bit,
34c49c3111SRichard Knutsson const s64 count, const u8 value, const bool is_rollback)
351da177e4SLinus Torvalds {
361da177e4SLinus Torvalds s64 cnt = count;
371da177e4SLinus Torvalds pgoff_t index, end_index;
381da177e4SLinus Torvalds struct address_space *mapping;
391da177e4SLinus Torvalds struct page *page;
401da177e4SLinus Torvalds u8 *kaddr;
411da177e4SLinus Torvalds int pos, len;
421da177e4SLinus Torvalds u8 bit;
431da177e4SLinus Torvalds
441da177e4SLinus Torvalds BUG_ON(!vi);
451da177e4SLinus Torvalds ntfs_debug("Entering for i_ino 0x%lx, start_bit 0x%llx, count 0x%llx, "
461da177e4SLinus Torvalds "value %u.%s", vi->i_ino, (unsigned long long)start_bit,
471da177e4SLinus Torvalds (unsigned long long)cnt, (unsigned int)value,
481da177e4SLinus Torvalds is_rollback ? " (rollback)" : "");
491da177e4SLinus Torvalds BUG_ON(start_bit < 0);
501da177e4SLinus Torvalds BUG_ON(cnt < 0);
511da177e4SLinus Torvalds BUG_ON(value > 1);
521da177e4SLinus Torvalds /*
531da177e4SLinus Torvalds * Calculate the indices for the pages containing the first and last
541da177e4SLinus Torvalds * bits, i.e. @start_bit and @start_bit + @cnt - 1, respectively.
551da177e4SLinus Torvalds */
5609cbfeafSKirill A. Shutemov index = start_bit >> (3 + PAGE_SHIFT);
5709cbfeafSKirill A. Shutemov end_index = (start_bit + cnt - 1) >> (3 + PAGE_SHIFT);
581da177e4SLinus Torvalds
591da177e4SLinus Torvalds /* Get the page containing the first bit (@start_bit). */
601da177e4SLinus Torvalds mapping = vi->i_mapping;
611da177e4SLinus Torvalds page = ntfs_map_page(mapping, index);
621da177e4SLinus Torvalds if (IS_ERR(page)) {
631da177e4SLinus Torvalds if (!is_rollback)
641da177e4SLinus Torvalds ntfs_error(vi->i_sb, "Failed to map first page (error "
651da177e4SLinus Torvalds "%li), aborting.", PTR_ERR(page));
661da177e4SLinus Torvalds return PTR_ERR(page);
671da177e4SLinus Torvalds }
681da177e4SLinus Torvalds kaddr = page_address(page);
691da177e4SLinus Torvalds
701da177e4SLinus Torvalds /* Set @pos to the position of the byte containing @start_bit. */
7109cbfeafSKirill A. Shutemov pos = (start_bit >> 3) & ~PAGE_MASK;
721da177e4SLinus Torvalds
731da177e4SLinus Torvalds /* Calculate the position of @start_bit in the first byte. */
741da177e4SLinus Torvalds bit = start_bit & 7;
751da177e4SLinus Torvalds
761da177e4SLinus Torvalds /* If the first byte is partial, modify the appropriate bits in it. */
771da177e4SLinus Torvalds if (bit) {
781da177e4SLinus Torvalds u8 *byte = kaddr + pos;
7918efefa9SAnton Altaparmakov while ((bit & 7) && cnt) {
8018efefa9SAnton Altaparmakov cnt--;
811da177e4SLinus Torvalds if (value)
821da177e4SLinus Torvalds *byte |= 1 << bit++;
831da177e4SLinus Torvalds else
841da177e4SLinus Torvalds *byte &= ~(1 << bit++);
851da177e4SLinus Torvalds }
861da177e4SLinus Torvalds /* If we are done, unmap the page and return success. */
871da177e4SLinus Torvalds if (!cnt)
881da177e4SLinus Torvalds goto done;
891da177e4SLinus Torvalds
901da177e4SLinus Torvalds /* Update @pos to the new position. */
911da177e4SLinus Torvalds pos++;
921da177e4SLinus Torvalds }
931da177e4SLinus Torvalds /*
941da177e4SLinus Torvalds * Depending on @value, modify all remaining whole bytes in the page up
951da177e4SLinus Torvalds * to @cnt.
961da177e4SLinus Torvalds */
9709cbfeafSKirill A. Shutemov len = min_t(s64, cnt >> 3, PAGE_SIZE - pos);
981da177e4SLinus Torvalds memset(kaddr + pos, value ? 0xff : 0, len);
991da177e4SLinus Torvalds cnt -= len << 3;
1001da177e4SLinus Torvalds
1011da177e4SLinus Torvalds /* Update @len to point to the first not-done byte in the page. */
1021da177e4SLinus Torvalds if (cnt < 8)
1031da177e4SLinus Torvalds len += pos;
1041da177e4SLinus Torvalds
1051da177e4SLinus Torvalds /* If we are not in the last page, deal with all subsequent pages. */
1061da177e4SLinus Torvalds while (index < end_index) {
1071da177e4SLinus Torvalds BUG_ON(cnt <= 0);
1081da177e4SLinus Torvalds
1091da177e4SLinus Torvalds /* Update @index and get the next page. */
1101da177e4SLinus Torvalds flush_dcache_page(page);
1111da177e4SLinus Torvalds set_page_dirty(page);
1121da177e4SLinus Torvalds ntfs_unmap_page(page);
1131da177e4SLinus Torvalds page = ntfs_map_page(mapping, ++index);
1141da177e4SLinus Torvalds if (IS_ERR(page))
1151da177e4SLinus Torvalds goto rollback;
1161da177e4SLinus Torvalds kaddr = page_address(page);
1171da177e4SLinus Torvalds /*
1181da177e4SLinus Torvalds * Depending on @value, modify all remaining whole bytes in the
1191da177e4SLinus Torvalds * page up to @cnt.
1201da177e4SLinus Torvalds */
12109cbfeafSKirill A. Shutemov len = min_t(s64, cnt >> 3, PAGE_SIZE);
1221da177e4SLinus Torvalds memset(kaddr, value ? 0xff : 0, len);
1231da177e4SLinus Torvalds cnt -= len << 3;
1241da177e4SLinus Torvalds }
1251da177e4SLinus Torvalds /*
1261da177e4SLinus Torvalds * The currently mapped page is the last one. If the last byte is
1271da177e4SLinus Torvalds * partial, modify the appropriate bits in it. Note, @len is the
1281da177e4SLinus Torvalds * position of the last byte inside the page.
1291da177e4SLinus Torvalds */
1301da177e4SLinus Torvalds if (cnt) {
1311da177e4SLinus Torvalds u8 *byte;
1321da177e4SLinus Torvalds
1331da177e4SLinus Torvalds BUG_ON(cnt > 7);
1341da177e4SLinus Torvalds
1351da177e4SLinus Torvalds bit = cnt;
1361da177e4SLinus Torvalds byte = kaddr + len;
1371da177e4SLinus Torvalds while (bit--) {
1381da177e4SLinus Torvalds if (value)
1391da177e4SLinus Torvalds *byte |= 1 << bit;
1401da177e4SLinus Torvalds else
1411da177e4SLinus Torvalds *byte &= ~(1 << bit);
1421da177e4SLinus Torvalds }
1431da177e4SLinus Torvalds }
1441da177e4SLinus Torvalds done:
1451da177e4SLinus Torvalds /* We are done. Unmap the page and return success. */
1461da177e4SLinus Torvalds flush_dcache_page(page);
1471da177e4SLinus Torvalds set_page_dirty(page);
1481da177e4SLinus Torvalds ntfs_unmap_page(page);
1491da177e4SLinus Torvalds ntfs_debug("Done.");
1501da177e4SLinus Torvalds return 0;
1511da177e4SLinus Torvalds rollback:
1521da177e4SLinus Torvalds /*
1531da177e4SLinus Torvalds * Current state:
1541da177e4SLinus Torvalds * - no pages are mapped
1551da177e4SLinus Torvalds * - @count - @cnt is the number of bits that have been modified
1561da177e4SLinus Torvalds */
1571da177e4SLinus Torvalds if (is_rollback)
1581da177e4SLinus Torvalds return PTR_ERR(page);
1591da177e4SLinus Torvalds if (count != cnt)
1601da177e4SLinus Torvalds pos = __ntfs_bitmap_set_bits_in_run(vi, start_bit, count - cnt,
161c49c3111SRichard Knutsson value ? 0 : 1, true);
1621da177e4SLinus Torvalds else
1631da177e4SLinus Torvalds pos = 0;
1641da177e4SLinus Torvalds if (!pos) {
1651da177e4SLinus Torvalds /* Rollback was successful. */
1661da177e4SLinus Torvalds ntfs_error(vi->i_sb, "Failed to map subsequent page (error "
1671da177e4SLinus Torvalds "%li), aborting.", PTR_ERR(page));
1681da177e4SLinus Torvalds } else {
1691da177e4SLinus Torvalds /* Rollback failed. */
1701da177e4SLinus Torvalds ntfs_error(vi->i_sb, "Failed to map subsequent page (error "
1711da177e4SLinus Torvalds "%li) and rollback failed (error %i). "
1721da177e4SLinus Torvalds "Aborting and leaving inconsistent metadata. "
1731da177e4SLinus Torvalds "Unmount and run chkdsk.", PTR_ERR(page), pos);
1741da177e4SLinus Torvalds NVolSetErrors(NTFS_SB(vi->i_sb));
1751da177e4SLinus Torvalds }
1761da177e4SLinus Torvalds return PTR_ERR(page);
1771da177e4SLinus Torvalds }
1781da177e4SLinus Torvalds
1791da177e4SLinus Torvalds #endif /* NTFS_RW */
180