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