1*1e9ea7e0SNamjae Jeon // SPDX-License-Identifier: GPL-2.0-or-later 2*1e9ea7e0SNamjae Jeon /* 3*1e9ea7e0SNamjae Jeon * bitmap.c - NTFS kernel bitmap handling. Part of the Linux-NTFS project. 4*1e9ea7e0SNamjae Jeon * 5*1e9ea7e0SNamjae Jeon * Copyright (c) 2004-2005 Anton Altaparmakov 6*1e9ea7e0SNamjae Jeon */ 7*1e9ea7e0SNamjae Jeon 8*1e9ea7e0SNamjae Jeon #ifdef NTFS_RW 9*1e9ea7e0SNamjae Jeon 10*1e9ea7e0SNamjae Jeon #include <linux/pagemap.h> 11*1e9ea7e0SNamjae Jeon 12*1e9ea7e0SNamjae Jeon #include "bitmap.h" 13*1e9ea7e0SNamjae Jeon #include "debug.h" 14*1e9ea7e0SNamjae Jeon #include "aops.h" 15*1e9ea7e0SNamjae Jeon #include "ntfs.h" 16*1e9ea7e0SNamjae Jeon 17*1e9ea7e0SNamjae Jeon /** 18*1e9ea7e0SNamjae Jeon * __ntfs_bitmap_set_bits_in_run - set a run of bits in a bitmap to a value 19*1e9ea7e0SNamjae Jeon * @vi: vfs inode describing the bitmap 20*1e9ea7e0SNamjae Jeon * @start_bit: first bit to set 21*1e9ea7e0SNamjae Jeon * @count: number of bits to set 22*1e9ea7e0SNamjae Jeon * @value: value to set the bits to (i.e. 0 or 1) 23*1e9ea7e0SNamjae Jeon * @is_rollback: if 'true' this is a rollback operation 24*1e9ea7e0SNamjae Jeon * 25*1e9ea7e0SNamjae Jeon * Set @count bits starting at bit @start_bit in the bitmap described by the 26*1e9ea7e0SNamjae Jeon * vfs inode @vi to @value, where @value is either 0 or 1. 27*1e9ea7e0SNamjae Jeon * 28*1e9ea7e0SNamjae Jeon * @is_rollback should always be 'false', it is for internal use to rollback 29*1e9ea7e0SNamjae Jeon * errors. You probably want to use ntfs_bitmap_set_bits_in_run() instead. 30*1e9ea7e0SNamjae Jeon * 31*1e9ea7e0SNamjae Jeon * Return 0 on success and -errno on error. 32*1e9ea7e0SNamjae Jeon */ 33*1e9ea7e0SNamjae Jeon int __ntfs_bitmap_set_bits_in_run(struct inode *vi, const s64 start_bit, 34*1e9ea7e0SNamjae Jeon const s64 count, const u8 value, const bool is_rollback) 35*1e9ea7e0SNamjae Jeon { 36*1e9ea7e0SNamjae Jeon s64 cnt = count; 37*1e9ea7e0SNamjae Jeon pgoff_t index, end_index; 38*1e9ea7e0SNamjae Jeon struct address_space *mapping; 39*1e9ea7e0SNamjae Jeon struct page *page; 40*1e9ea7e0SNamjae Jeon u8 *kaddr; 41*1e9ea7e0SNamjae Jeon int pos, len; 42*1e9ea7e0SNamjae Jeon u8 bit; 43*1e9ea7e0SNamjae Jeon 44*1e9ea7e0SNamjae Jeon BUG_ON(!vi); 45*1e9ea7e0SNamjae Jeon ntfs_debug("Entering for i_ino 0x%lx, start_bit 0x%llx, count 0x%llx, " 46*1e9ea7e0SNamjae Jeon "value %u.%s", vi->i_ino, (unsigned long long)start_bit, 47*1e9ea7e0SNamjae Jeon (unsigned long long)cnt, (unsigned int)value, 48*1e9ea7e0SNamjae Jeon is_rollback ? " (rollback)" : ""); 49*1e9ea7e0SNamjae Jeon BUG_ON(start_bit < 0); 50*1e9ea7e0SNamjae Jeon BUG_ON(cnt < 0); 51*1e9ea7e0SNamjae Jeon BUG_ON(value > 1); 52*1e9ea7e0SNamjae Jeon /* 53*1e9ea7e0SNamjae Jeon * Calculate the indices for the pages containing the first and last 54*1e9ea7e0SNamjae Jeon * bits, i.e. @start_bit and @start_bit + @cnt - 1, respectively. 55*1e9ea7e0SNamjae Jeon */ 56*1e9ea7e0SNamjae Jeon index = start_bit >> (3 + PAGE_SHIFT); 57*1e9ea7e0SNamjae Jeon end_index = (start_bit + cnt - 1) >> (3 + PAGE_SHIFT); 58*1e9ea7e0SNamjae Jeon 59*1e9ea7e0SNamjae Jeon /* Get the page containing the first bit (@start_bit). */ 60*1e9ea7e0SNamjae Jeon mapping = vi->i_mapping; 61*1e9ea7e0SNamjae Jeon page = ntfs_map_page(mapping, index); 62*1e9ea7e0SNamjae Jeon if (IS_ERR(page)) { 63*1e9ea7e0SNamjae Jeon if (!is_rollback) 64*1e9ea7e0SNamjae Jeon ntfs_error(vi->i_sb, "Failed to map first page (error " 65*1e9ea7e0SNamjae Jeon "%li), aborting.", PTR_ERR(page)); 66*1e9ea7e0SNamjae Jeon return PTR_ERR(page); 67*1e9ea7e0SNamjae Jeon } 68*1e9ea7e0SNamjae Jeon kaddr = page_address(page); 69*1e9ea7e0SNamjae Jeon 70*1e9ea7e0SNamjae Jeon /* Set @pos to the position of the byte containing @start_bit. */ 71*1e9ea7e0SNamjae Jeon pos = (start_bit >> 3) & ~PAGE_MASK; 72*1e9ea7e0SNamjae Jeon 73*1e9ea7e0SNamjae Jeon /* Calculate the position of @start_bit in the first byte. */ 74*1e9ea7e0SNamjae Jeon bit = start_bit & 7; 75*1e9ea7e0SNamjae Jeon 76*1e9ea7e0SNamjae Jeon /* If the first byte is partial, modify the appropriate bits in it. */ 77*1e9ea7e0SNamjae Jeon if (bit) { 78*1e9ea7e0SNamjae Jeon u8 *byte = kaddr + pos; 79*1e9ea7e0SNamjae Jeon while ((bit & 7) && cnt) { 80*1e9ea7e0SNamjae Jeon cnt--; 81*1e9ea7e0SNamjae Jeon if (value) 82*1e9ea7e0SNamjae Jeon *byte |= 1 << bit++; 83*1e9ea7e0SNamjae Jeon else 84*1e9ea7e0SNamjae Jeon *byte &= ~(1 << bit++); 85*1e9ea7e0SNamjae Jeon } 86*1e9ea7e0SNamjae Jeon /* If we are done, unmap the page and return success. */ 87*1e9ea7e0SNamjae Jeon if (!cnt) 88*1e9ea7e0SNamjae Jeon goto done; 89*1e9ea7e0SNamjae Jeon 90*1e9ea7e0SNamjae Jeon /* Update @pos to the new position. */ 91*1e9ea7e0SNamjae Jeon pos++; 92*1e9ea7e0SNamjae Jeon } 93*1e9ea7e0SNamjae Jeon /* 94*1e9ea7e0SNamjae Jeon * Depending on @value, modify all remaining whole bytes in the page up 95*1e9ea7e0SNamjae Jeon * to @cnt. 96*1e9ea7e0SNamjae Jeon */ 97*1e9ea7e0SNamjae Jeon len = min_t(s64, cnt >> 3, PAGE_SIZE - pos); 98*1e9ea7e0SNamjae Jeon memset(kaddr + pos, value ? 0xff : 0, len); 99*1e9ea7e0SNamjae Jeon cnt -= len << 3; 100*1e9ea7e0SNamjae Jeon 101*1e9ea7e0SNamjae Jeon /* Update @len to point to the first not-done byte in the page. */ 102*1e9ea7e0SNamjae Jeon if (cnt < 8) 103*1e9ea7e0SNamjae Jeon len += pos; 104*1e9ea7e0SNamjae Jeon 105*1e9ea7e0SNamjae Jeon /* If we are not in the last page, deal with all subsequent pages. */ 106*1e9ea7e0SNamjae Jeon while (index < end_index) { 107*1e9ea7e0SNamjae Jeon BUG_ON(cnt <= 0); 108*1e9ea7e0SNamjae Jeon 109*1e9ea7e0SNamjae Jeon /* Update @index and get the next page. */ 110*1e9ea7e0SNamjae Jeon flush_dcache_page(page); 111*1e9ea7e0SNamjae Jeon set_page_dirty(page); 112*1e9ea7e0SNamjae Jeon ntfs_unmap_page(page); 113*1e9ea7e0SNamjae Jeon page = ntfs_map_page(mapping, ++index); 114*1e9ea7e0SNamjae Jeon if (IS_ERR(page)) 115*1e9ea7e0SNamjae Jeon goto rollback; 116*1e9ea7e0SNamjae Jeon kaddr = page_address(page); 117*1e9ea7e0SNamjae Jeon /* 118*1e9ea7e0SNamjae Jeon * Depending on @value, modify all remaining whole bytes in the 119*1e9ea7e0SNamjae Jeon * page up to @cnt. 120*1e9ea7e0SNamjae Jeon */ 121*1e9ea7e0SNamjae Jeon len = min_t(s64, cnt >> 3, PAGE_SIZE); 122*1e9ea7e0SNamjae Jeon memset(kaddr, value ? 0xff : 0, len); 123*1e9ea7e0SNamjae Jeon cnt -= len << 3; 124*1e9ea7e0SNamjae Jeon } 125*1e9ea7e0SNamjae Jeon /* 126*1e9ea7e0SNamjae Jeon * The currently mapped page is the last one. If the last byte is 127*1e9ea7e0SNamjae Jeon * partial, modify the appropriate bits in it. Note, @len is the 128*1e9ea7e0SNamjae Jeon * position of the last byte inside the page. 129*1e9ea7e0SNamjae Jeon */ 130*1e9ea7e0SNamjae Jeon if (cnt) { 131*1e9ea7e0SNamjae Jeon u8 *byte; 132*1e9ea7e0SNamjae Jeon 133*1e9ea7e0SNamjae Jeon BUG_ON(cnt > 7); 134*1e9ea7e0SNamjae Jeon 135*1e9ea7e0SNamjae Jeon bit = cnt; 136*1e9ea7e0SNamjae Jeon byte = kaddr + len; 137*1e9ea7e0SNamjae Jeon while (bit--) { 138*1e9ea7e0SNamjae Jeon if (value) 139*1e9ea7e0SNamjae Jeon *byte |= 1 << bit; 140*1e9ea7e0SNamjae Jeon else 141*1e9ea7e0SNamjae Jeon *byte &= ~(1 << bit); 142*1e9ea7e0SNamjae Jeon } 143*1e9ea7e0SNamjae Jeon } 144*1e9ea7e0SNamjae Jeon done: 145*1e9ea7e0SNamjae Jeon /* We are done. Unmap the page and return success. */ 146*1e9ea7e0SNamjae Jeon flush_dcache_page(page); 147*1e9ea7e0SNamjae Jeon set_page_dirty(page); 148*1e9ea7e0SNamjae Jeon ntfs_unmap_page(page); 149*1e9ea7e0SNamjae Jeon ntfs_debug("Done."); 150*1e9ea7e0SNamjae Jeon return 0; 151*1e9ea7e0SNamjae Jeon rollback: 152*1e9ea7e0SNamjae Jeon /* 153*1e9ea7e0SNamjae Jeon * Current state: 154*1e9ea7e0SNamjae Jeon * - no pages are mapped 155*1e9ea7e0SNamjae Jeon * - @count - @cnt is the number of bits that have been modified 156*1e9ea7e0SNamjae Jeon */ 157*1e9ea7e0SNamjae Jeon if (is_rollback) 158*1e9ea7e0SNamjae Jeon return PTR_ERR(page); 159*1e9ea7e0SNamjae Jeon if (count != cnt) 160*1e9ea7e0SNamjae Jeon pos = __ntfs_bitmap_set_bits_in_run(vi, start_bit, count - cnt, 161*1e9ea7e0SNamjae Jeon value ? 0 : 1, true); 162*1e9ea7e0SNamjae Jeon else 163*1e9ea7e0SNamjae Jeon pos = 0; 164*1e9ea7e0SNamjae Jeon if (!pos) { 165*1e9ea7e0SNamjae Jeon /* Rollback was successful. */ 166*1e9ea7e0SNamjae Jeon ntfs_error(vi->i_sb, "Failed to map subsequent page (error " 167*1e9ea7e0SNamjae Jeon "%li), aborting.", PTR_ERR(page)); 168*1e9ea7e0SNamjae Jeon } else { 169*1e9ea7e0SNamjae Jeon /* Rollback failed. */ 170*1e9ea7e0SNamjae Jeon ntfs_error(vi->i_sb, "Failed to map subsequent page (error " 171*1e9ea7e0SNamjae Jeon "%li) and rollback failed (error %i). " 172*1e9ea7e0SNamjae Jeon "Aborting and leaving inconsistent metadata. " 173*1e9ea7e0SNamjae Jeon "Unmount and run chkdsk.", PTR_ERR(page), pos); 174*1e9ea7e0SNamjae Jeon NVolSetErrors(NTFS_SB(vi->i_sb)); 175*1e9ea7e0SNamjae Jeon } 176*1e9ea7e0SNamjae Jeon return PTR_ERR(page); 177*1e9ea7e0SNamjae Jeon } 178*1e9ea7e0SNamjae Jeon 179*1e9ea7e0SNamjae Jeon #endif /* NTFS_RW */ 180