xref: /linux/fs/ntfs/bitmap.c (revision 1e9ea7e04472d4e5e12e58c881eaacfb3e49b669)
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