fs: Add sdfat
[GitHub/LineageOS/android_kernel_samsung_universal7580.git] / fs / sdfat / fatent.c
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 {
53 u32 sec, off, _content;
54 u8 *fat_sector;
55 FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
56
57 /* fsi->vol_type == EXFAT */
58 sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-2));
59 off = (loc << 2) & (u32)(sb->s_blocksize - 1);
60
61 fat_sector = fcache_getblk(sb, sec);
62 if (!fat_sector)
63 return -EIO;
64
65 _content = le32_to_cpu(*(__le32 *)(&fat_sector[off]));
66
67 /* remap reserved clusters to simplify code */
68 if (_content >= CLUSTER_32(0xFFFFFFF8))
69 _content = CLUS_EOF;
70
71 *content = CLUSTER_32(_content);
72 return 0;
73 }
74
75 static s32 exfat_ent_set(struct super_block *sb, u32 loc, u32 content)
76 {
77 u32 sec, off;
78 u8 *fat_sector;
79 __le32 *fat_entry;
80 FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
81
82 sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-2));
83 off = (loc << 2) & (u32)(sb->s_blocksize - 1);
84
85 fat_sector = fcache_getblk(sb, sec);
86 if (!fat_sector)
87 return -EIO;
88
89 fat_entry = (__le32 *)&(fat_sector[off]);
90 *fat_entry = cpu_to_le32(content);
91
92 return fcache_modify(sb, sec);
93 }
94
95 #define FATENT_FAT32_VALID_MASK (0x0FFFFFFFU)
96 #define FATENT_FAT32_IGNORE_MASK (0xF0000000U)
97 static s32 fat32_ent_get(struct super_block *sb, u32 loc, u32 *content)
98 {
99 u32 sec, off, _content;
100 u8 *fat_sector;
101 FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
102
103 sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-2));
104 off = (loc << 2) & (u32)(sb->s_blocksize - 1);
105
106 fat_sector = fcache_getblk(sb, sec);
107 if (!fat_sector)
108 return -EIO;
109
110 _content = le32_to_cpu(*(__le32 *)(&fat_sector[off]));
111 _content &= FATENT_FAT32_VALID_MASK;
112
113 /* remap reserved clusters to simplify code */
114 if (_content == CLUSTER_32(0x0FFFFFF7U))
115 _content = CLUS_BAD;
116 else if (_content >= CLUSTER_32(0x0FFFFFF8U))
117 _content = CLUS_EOF;
118
119 *content = CLUSTER_32(_content);
120 return 0;
121 }
122
123 static s32 fat32_ent_set(struct super_block *sb, u32 loc, u32 content)
124 {
125 u32 sec, off;
126 u8 *fat_sector;
127 __le32 *fat_entry;
128 FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
129
130 content &= FATENT_FAT32_VALID_MASK;
131
132 sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-2));
133 off = (loc << 2) & (u32)(sb->s_blocksize - 1);
134
135 fat_sector = fcache_getblk(sb, sec);
136 if (!fat_sector)
137 return -EIO;
138
139 fat_entry = (__le32 *)&(fat_sector[off]);
140 content |= (le32_to_cpu(*fat_entry) & FATENT_FAT32_IGNORE_MASK);
141 *fat_entry = cpu_to_le32(content);
142
143 return fcache_modify(sb, sec);
144 }
145
146 #define FATENT_FAT16_VALID_MASK (0x0000FFFFU)
147 static s32 fat16_ent_get(struct super_block *sb, u32 loc, u32 *content)
148 {
149 u32 sec, off, _content;
150 u8 *fat_sector;
151 FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
152
153 sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-1));
154 off = (loc << 1) & (u32)(sb->s_blocksize - 1);
155
156 fat_sector = fcache_getblk(sb, sec);
157 if (!fat_sector)
158 return -EIO;
159
160 _content = (u32)le16_to_cpu(*(__le16 *)(&fat_sector[off]));
161 _content &= FATENT_FAT16_VALID_MASK;
162
163 /* remap reserved clusters to simplify code */
164 if (_content == CLUSTER_16(0xFFF7U))
165 _content = CLUS_BAD;
166 else if (_content >= CLUSTER_16(0xFFF8U))
167 _content = CLUS_EOF;
168
169 *content = CLUSTER_32(_content);
170 return 0;
171 }
172
173 static s32 fat16_ent_set(struct super_block *sb, u32 loc, u32 content)
174 {
175 u32 sec, off;
176 u8 *fat_sector;
177 __le16 *fat_entry;
178 FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
179
180 content &= FATENT_FAT16_VALID_MASK;
181
182 sec = fsi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-1));
183 off = (loc << 1) & (u32)(sb->s_blocksize - 1);
184
185 fat_sector = fcache_getblk(sb, sec);
186 if (!fat_sector)
187 return -EIO;
188
189 fat_entry = (__le16 *)&(fat_sector[off]);
190 *fat_entry = cpu_to_le16(content);
191
192 return fcache_modify(sb, sec);
193 }
194
195 #define FATENT_FAT12_VALID_MASK (0x00000FFFU)
196 static s32 fat12_ent_get(struct super_block *sb, u32 loc, u32 *content)
197 {
198 u32 sec, off, _content;
199 u8 *fat_sector;
200 FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
201
202 sec = fsi->FAT1_start_sector + ((loc + (loc >> 1)) >> sb->s_blocksize_bits);
203 off = (loc + (loc >> 1)) & (u32)(sb->s_blocksize - 1);
204
205 fat_sector = fcache_getblk(sb, sec);
206 if (!fat_sector)
207 return -EIO;
208
209 if (off == (u32)(sb->s_blocksize - 1)) {
210 _content = (u32) fat_sector[off];
211
212 fat_sector = fcache_getblk(sb, ++sec);
213 if (!fat_sector)
214 return -EIO;
215
216 _content |= (u32) fat_sector[0] << 8;
217 } else {
218 _content = get_unaligned_le16(&fat_sector[off]);
219 }
220
221 if (loc & 1)
222 _content >>= 4;
223
224 _content &= FATENT_FAT12_VALID_MASK;
225
226 /* remap reserved clusters to simplify code */
227 if (_content == CLUSTER_16(0x0FF7U))
228 _content = CLUS_BAD;
229 else if (_content >= CLUSTER_16(0x0FF8U))
230 _content = CLUS_EOF;
231
232 *content = CLUSTER_32(_content);
233 return 0;
234 }
235
236 static s32 fat12_ent_set(struct super_block *sb, u32 loc, u32 content)
237 {
238 u32 sec, off;
239 u8 *fat_sector, *fat_entry;
240 FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
241
242 content &= FATENT_FAT12_VALID_MASK;
243
244 sec = fsi->FAT1_start_sector + ((loc + (loc >> 1)) >> sb->s_blocksize_bits);
245 off = (loc + (loc >> 1)) & (u32)(sb->s_blocksize - 1);
246
247 fat_sector = fcache_getblk(sb, sec);
248 if (!fat_sector)
249 return -EIO;
250
251 if (loc & 1) { /* odd */
252
253 content <<= 4;
254
255 if (off == (u32)(sb->s_blocksize-1)) {
256 fat_sector[off] = (u8)(content | (fat_sector[off] & 0x0F));
257 if (fcache_modify(sb, sec))
258 return -EIO;
259
260 fat_sector = fcache_getblk(sb, ++sec);
261 if (!fat_sector)
262 return -EIO;
263
264 fat_sector[0] = (u8)(content >> 8);
265 } else {
266 fat_entry = &(fat_sector[off]);
267 content |= 0x000F & get_unaligned_le16(fat_entry);
268 put_unaligned_le16(content, fat_entry);
269 }
270 } else { /* even */
271 fat_sector[off] = (u8)(content);
272
273 if (off == (u32)(sb->s_blocksize-1)) {
274 fat_sector[off] = (u8)(content);
275 if (fcache_modify(sb, sec))
276 return -EIO;
277
278 fat_sector = fcache_getblk(sb, ++sec);
279 if (!fat_sector)
280 return -EIO;
281
282 fat_sector[0] = (u8)((fat_sector[0] & 0xF0) | (content >> 8));
283 } else {
284 fat_entry = &(fat_sector[off]);
285 content |= 0xF000 & get_unaligned_le16(fat_entry);
286 put_unaligned_le16(content, fat_entry);
287 }
288 }
289 return fcache_modify(sb, sec);
290 }
291
292
293 static FATENT_OPS_T fat12_ent_ops = {
294 fat12_ent_get,
295 fat12_ent_set
296 };
297
298 static FATENT_OPS_T fat16_ent_ops = {
299 fat16_ent_get,
300 fat16_ent_set
301 };
302
303 static FATENT_OPS_T fat32_ent_ops = {
304 fat32_ent_get,
305 fat32_ent_set
306 };
307
308 static FATENT_OPS_T exfat_ent_ops = {
309 exfat_ent_get,
310 exfat_ent_set
311 };
312
313 s32 fat_ent_ops_init(struct super_block *sb)
314 {
315 FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
316
317 switch (fsi->vol_type) {
318 case EXFAT:
319 fsi->fatent_ops = &exfat_ent_ops;
320 break;
321 case FAT32:
322 fsi->fatent_ops = &fat32_ent_ops;
323 break;
324 case FAT16:
325 fsi->fatent_ops = &fat16_ent_ops;
326 break;
327 case FAT12:
328 fsi->fatent_ops = &fat12_ent_ops;
329 break;
330 default:
331 fsi->fatent_ops = NULL;
332 EMSG("Unknown volume type : %d", (int)fsi->vol_type);
333 return -ENOTSUPP;
334 }
335
336 return 0;
337 }
338
339 static inline bool is_reserved_clus(u32 clus)
340 {
341 if (IS_CLUS_FREE(clus))
342 return true;
343 if (IS_CLUS_EOF(clus))
344 return true;
345 if (IS_CLUS_BAD(clus))
346 return true;
347 return false;
348 }
349
350 static inline bool is_valid_clus(FS_INFO_T *fsi, u32 clus)
351 {
352 if (clus < CLUS_BASE || fsi->num_clusters <= clus)
353 return false;
354 return true;
355 }
356
357 s32 fat_ent_get(struct super_block *sb, u32 loc, u32 *content)
358 {
359 FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
360 s32 err;
361
362 if (!is_valid_clus(fsi, loc)) {
363 sdfat_fs_error(sb, "invalid access to FAT (entry 0x%08x)", loc);
364 return -EIO;
365 }
366
367 err = fsi->fatent_ops->ent_get(sb, loc, content);
368 if (err) {
369 sdfat_fs_error(sb, "failed to access to FAT "
370 "(entry 0x%08x, err:%d)", loc, err);
371 return err;
372 }
373
374 if (!is_reserved_clus(*content) && !is_valid_clus(fsi, *content)) {
375 sdfat_fs_error(sb, "invalid access to FAT (entry 0x%08x) "
376 "bogus content (0x%08x)", loc, *content);
377 return -EIO;
378 }
379
380 return 0;
381 }
382
383 s32 fat_ent_set(struct super_block *sb, u32 loc, u32 content)
384 {
385 FS_INFO_T *fsi = &(SDFAT_SB(sb)->fsi);
386
387 return fsi->fatent_ops->ent_set(sb, loc, content);
388 }
389
390 s32 fat_ent_get_safe(struct super_block *sb, u32 loc, u32 *content)
391 {
392 s32 err = fat_ent_get(sb, loc, content);
393
394 if (err)
395 return err;
396
397 if (IS_CLUS_FREE(*content)) {
398 sdfat_fs_error(sb, "invalid access to FAT free cluster "
399 "(entry 0x%08x)", loc);
400 return -EIO;
401 }
402
403 if (IS_CLUS_BAD(*content)) {
404 sdfat_fs_error(sb, "invalid access to FAT bad cluster "
405 "(entry 0x%08x)", loc);
406 return -EIO;
407 }
408
409 return 0;
410 }
411
412 /* end of fatent.c */