diff options
Diffstat (limited to 'fs/ntfs/bitmap.c')
-rw-r--r-- | fs/ntfs/bitmap.c | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/fs/ntfs/bitmap.c b/fs/ntfs/bitmap.c new file mode 100644 index 000000000000..12cf2e30c7dd --- /dev/null +++ b/fs/ntfs/bitmap.c | |||
@@ -0,0 +1,192 @@ | |||
1 | /* | ||
2 | * bitmap.c - NTFS kernel bitmap handling. Part of the Linux-NTFS project. | ||
3 | * | ||
4 | * Copyright (c) 2004 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_CACHE_SHIFT); | ||
71 | end_index = (start_bit + cnt - 1) >> (3 + PAGE_CACHE_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_CACHE_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 | if (value) | ||
95 | *byte |= 1 << bit++; | ||
96 | else | ||
97 | *byte &= ~(1 << bit++); | ||
98 | } | ||
99 | /* If we are done, unmap the page and return success. */ | ||
100 | if (!cnt) | ||
101 | goto done; | ||
102 | |||
103 | /* Update @pos to the new position. */ | ||
104 | pos++; | ||
105 | } | ||
106 | /* | ||
107 | * Depending on @value, modify all remaining whole bytes in the page up | ||
108 | * to @cnt. | ||
109 | */ | ||
110 | len = min_t(s64, cnt >> 3, PAGE_CACHE_SIZE - pos); | ||
111 | memset(kaddr + pos, value ? 0xff : 0, len); | ||
112 | cnt -= len << 3; | ||
113 | |||
114 | /* Update @len to point to the first not-done byte in the page. */ | ||
115 | if (cnt < 8) | ||
116 | len += pos; | ||
117 | |||
118 | /* If we are not in the last page, deal with all subsequent pages. */ | ||
119 | while (index < end_index) { | ||
120 | BUG_ON(cnt <= 0); | ||
121 | |||
122 | /* Update @index and get the next page. */ | ||
123 | flush_dcache_page(page); | ||
124 | set_page_dirty(page); | ||
125 | ntfs_unmap_page(page); | ||
126 | page = ntfs_map_page(mapping, ++index); | ||
127 | if (IS_ERR(page)) | ||
128 | goto rollback; | ||
129 | kaddr = page_address(page); | ||
130 | /* | ||
131 | * Depending on @value, modify all remaining whole bytes in the | ||
132 | * page up to @cnt. | ||
133 | */ | ||
134 | len = min_t(s64, cnt >> 3, PAGE_CACHE_SIZE); | ||
135 | memset(kaddr, value ? 0xff : 0, len); | ||
136 | cnt -= len << 3; | ||
137 | } | ||
138 | /* | ||
139 | * The currently mapped page is the last one. If the last byte is | ||
140 | * partial, modify the appropriate bits in it. Note, @len is the | ||
141 | * position of the last byte inside the page. | ||
142 | */ | ||
143 | if (cnt) { | ||
144 | u8 *byte; | ||
145 | |||
146 | BUG_ON(cnt > 7); | ||
147 | |||
148 | bit = cnt; | ||
149 | byte = kaddr + len; | ||
150 | while (bit--) { | ||
151 | if (value) | ||
152 | *byte |= 1 << bit; | ||
153 | else | ||
154 | *byte &= ~(1 << bit); | ||
155 | } | ||
156 | } | ||
157 | done: | ||
158 | /* We are done. Unmap the page and return success. */ | ||
159 | flush_dcache_page(page); | ||
160 | set_page_dirty(page); | ||
161 | ntfs_unmap_page(page); | ||
162 | ntfs_debug("Done."); | ||
163 | return 0; | ||
164 | rollback: | ||
165 | /* | ||
166 | * Current state: | ||
167 | * - no pages are mapped | ||
168 | * - @count - @cnt is the number of bits that have been modified | ||
169 | */ | ||
170 | if (is_rollback) | ||
171 | return PTR_ERR(page); | ||
172 | if (count != cnt) | ||
173 | pos = __ntfs_bitmap_set_bits_in_run(vi, start_bit, count - cnt, | ||
174 | value ? 0 : 1, TRUE); | ||
175 | else | ||
176 | pos = 0; | ||
177 | if (!pos) { | ||
178 | /* Rollback was successful. */ | ||
179 | ntfs_error(vi->i_sb, "Failed to map subsequent page (error " | ||
180 | "%li), aborting.", PTR_ERR(page)); | ||
181 | } else { | ||
182 | /* Rollback failed. */ | ||
183 | ntfs_error(vi->i_sb, "Failed to map subsequent page (error " | ||
184 | "%li) and rollback failed (error %i). " | ||
185 | "Aborting and leaving inconsistent metadata. " | ||
186 | "Unmount and run chkdsk.", PTR_ERR(page), pos); | ||
187 | NVolSetErrors(NTFS_SB(vi->i_sb)); | ||
188 | } | ||
189 | return PTR_ERR(page); | ||
190 | } | ||
191 | |||
192 | #endif /* NTFS_RW */ | ||