fs: sdfat: Update to version 2.4.5
[GitHub/LineageOS/android_kernel_samsung_universal7580.git] / fs / sdfat / fatent.c
CommitLineData
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 */
51static 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
76static 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)
99static 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
126static 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)
151static 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
178static 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)
202static 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
243static 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
301static FATENT_OPS_T fat12_ent_ops = {
302 fat12_ent_get,
303 fat12_ent_set
304};
305
306static FATENT_OPS_T fat16_ent_ops = {
307 fat16_ent_get,
308 fat16_ent_set
309};
310
311static FATENT_OPS_T fat32_ent_ops = {
312 fat32_ent_get,
313 fat32_ent_set
314};
315
316static FATENT_OPS_T exfat_ent_ops = {
317 exfat_ent_get,
318 exfat_ent_set
319};
320
321s32 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
347static 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
358s32 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
384s32 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
391s32 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 */