Commit | Line | Data |
---|---|---|
45e430a1 PK |
1 | /* |
2 | * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or | |
5 | * modify it under the terms of the GNU General Public License | |
6 | * as published by the Free Software Foundation; either version 2 | |
7 | * of the License, or (at your option) any later version. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
16 | */ | |
17 | ||
18 | /************************************************************************/ | |
19 | /* */ | |
20 | /* PROJECT : exFAT & FAT12/16/32 File System */ | |
21 | /* FILE : fatent.c */ | |
22 | /* PURPOSE : sdFAT FAT entry manager */ | |
23 | /* */ | |
24 | /*----------------------------------------------------------------------*/ | |
25 | /* NOTES */ | |
26 | /* */ | |
27 | /* */ | |
28 | /************************************************************************/ | |
29 | ||
30 | #include <asm/unaligned.h> | |
31 | ||
32 | #include "sdfat.h" | |
33 | #include "core.h" | |
34 | ||
35 | /*----------------------------------------------------------------------*/ | |
36 | /* Global Variable Definitions */ | |
37 | /*----------------------------------------------------------------------*/ | |
38 | /* All buffer structures are protected w/ fsi->v_sem */ | |
39 | ||
40 | /*----------------------------------------------------------------------*/ | |
41 | /* Static functions */ | |
42 | /*----------------------------------------------------------------------*/ | |
43 | ||
44 | /*======================================================================*/ | |
45 | /* FAT Read/Write Functions */ | |
46 | /*======================================================================*/ | |
47 | /* in : sb, loc | |
48 | * out: content | |
49 | * returns 0 on success, -1 on error | |
50 | */ | |
51 | static s32 exfat_ent_get(struct super_block *sb, u32 loc, u32 *content) | |
52 | { | |
e9692b33 PK |
53 | u32 off, _content; |
54 | u64 sec; | |
45e430a1 PK |
55 | u8 *fat_sector; |
56 | FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); | |
57 | ||
58 | /* fsi->vol_type == EXFAT */ | |
59 | sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-2)); | |
60 | off = (loc << 2) & (u32)(sb->s_blocksize - 1); | |
61 | ||
62 | fat_sector = fcache_getblk(sb, sec); | |
63 | if (!fat_sector) | |
64 | return -EIO; | |
65 | ||
66 | _content = le32_to_cpu(*(__le32 *)(&fat_sector[off])); | |
67 | ||
68 | /* remap reserved clusters to simplify code */ | |
69 | if (_content >= CLUSTER_32(0xFFFFFFF8)) | |
70 | _content = CLUS_EOF; | |
71 | ||
72 | *content = CLUSTER_32(_content); | |
73 | return 0; | |
74 | } | |
75 | ||
76 | static s32 exfat_ent_set(struct super_block *sb, u32 loc, u32 content) | |
77 | { | |
e9692b33 PK |
78 | u32 off; |
79 | u64 sec; | |
45e430a1 PK |
80 | u8 *fat_sector; |
81 | __le32 *fat_entry; | |
82 | FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); | |
83 | ||
84 | sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-2)); | |
85 | off = (loc << 2) & (u32)(sb->s_blocksize - 1); | |
86 | ||
87 | fat_sector = fcache_getblk(sb, sec); | |
88 | if (!fat_sector) | |
89 | return -EIO; | |
90 | ||
91 | fat_entry = (__le32 *)&(fat_sector[off]); | |
92 | *fat_entry = cpu_to_le32(content); | |
93 | ||
94 | return fcache_modify(sb, sec); | |
95 | } | |
96 | ||
97 | #define FATENT_FAT32_VALID_MASK (0x0FFFFFFFU) | |
98 | #define FATENT_FAT32_IGNORE_MASK (0xF0000000U) | |
99 | static s32 fat32_ent_get(struct super_block *sb, u32 loc, u32 *content) | |
100 | { | |
e9692b33 PK |
101 | u32 off, _content; |
102 | u64 sec; | |
45e430a1 PK |
103 | u8 *fat_sector; |
104 | FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); | |
105 | ||
106 | sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-2)); | |
107 | off = (loc << 2) & (u32)(sb->s_blocksize - 1); | |
108 | ||
109 | fat_sector = fcache_getblk(sb, sec); | |
110 | if (!fat_sector) | |
111 | return -EIO; | |
112 | ||
113 | _content = le32_to_cpu(*(__le32 *)(&fat_sector[off])); | |
114 | _content &= FATENT_FAT32_VALID_MASK; | |
115 | ||
116 | /* remap reserved clusters to simplify code */ | |
117 | if (_content == CLUSTER_32(0x0FFFFFF7U)) | |
118 | _content = CLUS_BAD; | |
119 | else if (_content >= CLUSTER_32(0x0FFFFFF8U)) | |
120 | _content = CLUS_EOF; | |
121 | ||
122 | *content = CLUSTER_32(_content); | |
123 | return 0; | |
124 | } | |
125 | ||
126 | static s32 fat32_ent_set(struct super_block *sb, u32 loc, u32 content) | |
127 | { | |
e9692b33 PK |
128 | u32 off; |
129 | u64 sec; | |
45e430a1 PK |
130 | u8 *fat_sector; |
131 | __le32 *fat_entry; | |
132 | FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); | |
133 | ||
134 | content &= FATENT_FAT32_VALID_MASK; | |
135 | ||
136 | sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-2)); | |
137 | off = (loc << 2) & (u32)(sb->s_blocksize - 1); | |
138 | ||
139 | fat_sector = fcache_getblk(sb, sec); | |
140 | if (!fat_sector) | |
141 | return -EIO; | |
142 | ||
143 | fat_entry = (__le32 *)&(fat_sector[off]); | |
144 | content |= (le32_to_cpu(*fat_entry) & FATENT_FAT32_IGNORE_MASK); | |
145 | *fat_entry = cpu_to_le32(content); | |
146 | ||
147 | return fcache_modify(sb, sec); | |
148 | } | |
149 | ||
150 | #define FATENT_FAT16_VALID_MASK (0x0000FFFFU) | |
151 | static s32 fat16_ent_get(struct super_block *sb, u32 loc, u32 *content) | |
152 | { | |
e9692b33 PK |
153 | u32 off, _content; |
154 | u64 sec; | |
45e430a1 PK |
155 | u8 *fat_sector; |
156 | FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); | |
157 | ||
158 | sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-1)); | |
159 | off = (loc << 1) & (u32)(sb->s_blocksize - 1); | |
160 | ||
161 | fat_sector = fcache_getblk(sb, sec); | |
162 | if (!fat_sector) | |
163 | return -EIO; | |
164 | ||
165 | _content = (u32)le16_to_cpu(*(__le16 *)(&fat_sector[off])); | |
166 | _content &= FATENT_FAT16_VALID_MASK; | |
167 | ||
168 | /* remap reserved clusters to simplify code */ | |
169 | if (_content == CLUSTER_16(0xFFF7U)) | |
170 | _content = CLUS_BAD; | |
171 | else if (_content >= CLUSTER_16(0xFFF8U)) | |
172 | _content = CLUS_EOF; | |
173 | ||
174 | *content = CLUSTER_32(_content); | |
175 | return 0; | |
176 | } | |
177 | ||
178 | static s32 fat16_ent_set(struct super_block *sb, u32 loc, u32 content) | |
179 | { | |
e9692b33 PK |
180 | u32 off; |
181 | u64 sec; | |
45e430a1 PK |
182 | u8 *fat_sector; |
183 | __le16 *fat_entry; | |
184 | FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); | |
185 | ||
186 | content &= FATENT_FAT16_VALID_MASK; | |
187 | ||
188 | sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-1)); | |
189 | off = (loc << 1) & (u32)(sb->s_blocksize - 1); | |
190 | ||
191 | fat_sector = fcache_getblk(sb, sec); | |
192 | if (!fat_sector) | |
193 | return -EIO; | |
194 | ||
195 | fat_entry = (__le16 *)&(fat_sector[off]); | |
196 | *fat_entry = cpu_to_le16(content); | |
197 | ||
198 | return fcache_modify(sb, sec); | |
199 | } | |
200 | ||
201 | #define FATENT_FAT12_VALID_MASK (0x00000FFFU) | |
202 | static s32 fat12_ent_get(struct super_block *sb, u32 loc, u32 *content) | |
203 | { | |
e9692b33 PK |
204 | u32 off, _content; |
205 | u64 sec; | |
45e430a1 PK |
206 | u8 *fat_sector; |
207 | FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); | |
208 | ||
209 | sec = fsi->FAT1_start_sector + ((loc + (loc >> 1)) >> sb->s_blocksize_bits); | |
210 | off = (loc + (loc >> 1)) & (u32)(sb->s_blocksize - 1); | |
211 | ||
212 | fat_sector = fcache_getblk(sb, sec); | |
213 | if (!fat_sector) | |
214 | return -EIO; | |
215 | ||
216 | if (off == (u32)(sb->s_blocksize - 1)) { | |
217 | _content = (u32) fat_sector[off]; | |
218 | ||
219 | fat_sector = fcache_getblk(sb, ++sec); | |
220 | if (!fat_sector) | |
221 | return -EIO; | |
222 | ||
223 | _content |= (u32) fat_sector[0] << 8; | |
224 | } else { | |
225 | _content = get_unaligned_le16(&fat_sector[off]); | |
226 | } | |
227 | ||
228 | if (loc & 1) | |
229 | _content >>= 4; | |
230 | ||
231 | _content &= FATENT_FAT12_VALID_MASK; | |
232 | ||
233 | /* remap reserved clusters to simplify code */ | |
234 | if (_content == CLUSTER_16(0x0FF7U)) | |
235 | _content = CLUS_BAD; | |
236 | else if (_content >= CLUSTER_16(0x0FF8U)) | |
237 | _content = CLUS_EOF; | |
238 | ||
239 | *content = CLUSTER_32(_content); | |
240 | return 0; | |
241 | } | |
242 | ||
243 | static s32 fat12_ent_set(struct super_block *sb, u32 loc, u32 content) | |
244 | { | |
e9692b33 PK |
245 | u32 off; |
246 | u64 sec; | |
45e430a1 PK |
247 | u8 *fat_sector, *fat_entry; |
248 | FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); | |
249 | ||
250 | content &= FATENT_FAT12_VALID_MASK; | |
251 | ||
252 | sec = fsi->FAT1_start_sector + ((loc + (loc >> 1)) >> sb->s_blocksize_bits); | |
253 | off = (loc + (loc >> 1)) & (u32)(sb->s_blocksize - 1); | |
254 | ||
255 | fat_sector = fcache_getblk(sb, sec); | |
256 | if (!fat_sector) | |
257 | return -EIO; | |
258 | ||
259 | if (loc & 1) { /* odd */ | |
260 | ||
261 | content <<= 4; | |
262 | ||
263 | if (off == (u32)(sb->s_blocksize-1)) { | |
264 | fat_sector[off] = (u8)(content | (fat_sector[off] & 0x0F)); | |
265 | if (fcache_modify(sb, sec)) | |
266 | return -EIO; | |
267 | ||
268 | fat_sector = fcache_getblk(sb, ++sec); | |
269 | if (!fat_sector) | |
270 | return -EIO; | |
271 | ||
272 | fat_sector[0] = (u8)(content >> 8); | |
273 | } else { | |
274 | fat_entry = &(fat_sector[off]); | |
275 | content |= 0x000F & get_unaligned_le16(fat_entry); | |
276 | put_unaligned_le16(content, fat_entry); | |
277 | } | |
278 | } else { /* even */ | |
279 | fat_sector[off] = (u8)(content); | |
280 | ||
281 | if (off == (u32)(sb->s_blocksize-1)) { | |
282 | fat_sector[off] = (u8)(content); | |
283 | if (fcache_modify(sb, sec)) | |
284 | return -EIO; | |
285 | ||
286 | fat_sector = fcache_getblk(sb, ++sec); | |
287 | if (!fat_sector) | |
288 | return -EIO; | |
289 | ||
290 | fat_sector[0] = (u8)((fat_sector[0] & 0xF0) | (content >> 8)); | |
291 | } else { | |
292 | fat_entry = &(fat_sector[off]); | |
293 | content |= 0xF000 & get_unaligned_le16(fat_entry); | |
294 | put_unaligned_le16(content, fat_entry); | |
295 | } | |
296 | } | |
297 | return fcache_modify(sb, sec); | |
298 | } | |
299 | ||
300 | ||
301 | static FATENT_OPS_T fat12_ent_ops = { | |
302 | fat12_ent_get, | |
303 | fat12_ent_set | |
304 | }; | |
305 | ||
306 | static FATENT_OPS_T fat16_ent_ops = { | |
307 | fat16_ent_get, | |
308 | fat16_ent_set | |
309 | }; | |
310 | ||
311 | static FATENT_OPS_T fat32_ent_ops = { | |
312 | fat32_ent_get, | |
313 | fat32_ent_set | |
314 | }; | |
315 | ||
316 | static FATENT_OPS_T exfat_ent_ops = { | |
317 | exfat_ent_get, | |
318 | exfat_ent_set | |
319 | }; | |
320 | ||
321 | s32 fat_ent_ops_init(struct super_block *sb) | |
322 | { | |
323 | FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); | |
324 | ||
325 | switch (fsi->vol_type) { | |
326 | case EXFAT: | |
327 | fsi->fatent_ops = &exfat_ent_ops; | |
328 | break; | |
329 | case FAT32: | |
330 | fsi->fatent_ops = &fat32_ent_ops; | |
331 | break; | |
332 | case FAT16: | |
333 | fsi->fatent_ops = &fat16_ent_ops; | |
334 | break; | |
335 | case FAT12: | |
336 | fsi->fatent_ops = &fat12_ent_ops; | |
337 | break; | |
338 | default: | |
339 | fsi->fatent_ops = NULL; | |
340 | EMSG("Unknown volume type : %d", (int)fsi->vol_type); | |
341 | return -ENOTSUPP; | |
342 | } | |
343 | ||
344 | return 0; | |
345 | } | |
346 | ||
347 | static inline bool is_reserved_clus(u32 clus) | |
348 | { | |
349 | if (IS_CLUS_FREE(clus)) | |
350 | return true; | |
351 | if (IS_CLUS_EOF(clus)) | |
352 | return true; | |
353 | if (IS_CLUS_BAD(clus)) | |
354 | return true; | |
355 | return false; | |
356 | } | |
357 | ||
45e430a1 PK |
358 | s32 fat_ent_get(struct super_block *sb, u32 loc, u32 *content) |
359 | { | |
360 | FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); | |
361 | s32 err; | |
362 | ||
363 | if (!is_valid_clus(fsi, loc)) { | |
364 | sdfat_fs_error(sb, "invalid access to FAT (entry 0x%08x)", loc); | |
365 | return -EIO; | |
366 | } | |
367 | ||
368 | err = fsi->fatent_ops->ent_get(sb, loc, content); | |
369 | if (err) { | |
370 | sdfat_fs_error(sb, "failed to access to FAT " | |
371 | "(entry 0x%08x, err:%d)", loc, err); | |
372 | return err; | |
373 | } | |
374 | ||
375 | if (!is_reserved_clus(*content) && !is_valid_clus(fsi, *content)) { | |
376 | sdfat_fs_error(sb, "invalid access to FAT (entry 0x%08x) " | |
377 | "bogus content (0x%08x)", loc, *content); | |
378 | return -EIO; | |
379 | } | |
380 | ||
381 | return 0; | |
382 | } | |
383 | ||
384 | s32 fat_ent_set(struct super_block *sb, u32 loc, u32 content) | |
385 | { | |
386 | FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi); | |
387 | ||
388 | return fsi->fatent_ops->ent_set(sb, loc, content); | |
389 | } | |
390 | ||
391 | s32 fat_ent_get_safe(struct super_block *sb, u32 loc, u32 *content) | |
392 | { | |
393 | s32 err = fat_ent_get(sb, loc, content); | |
394 | ||
395 | if (err) | |
396 | return err; | |
397 | ||
398 | if (IS_CLUS_FREE(*content)) { | |
399 | sdfat_fs_error(sb, "invalid access to FAT free cluster " | |
400 | "(entry 0x%08x)", loc); | |
401 | return -EIO; | |
402 | } | |
403 | ||
404 | if (IS_CLUS_BAD(*content)) { | |
405 | sdfat_fs_error(sb, "invalid access to FAT bad cluster " | |
406 | "(entry 0x%08x)", loc); | |
407 | return -EIO; | |
408 | } | |
409 | ||
410 | return 0; | |
411 | } | |
412 | ||
413 | /* end of fatent.c */ |