fs: Add sdfat
[GitHub/LineageOS/android_kernel_samsung_universal7580.git] / fs / sdfat / misc.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 * linux/fs/fat/misc.c
20 *
21 * Written 1992,1993 by Werner Almesberger
22 * 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980
23 * and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru)
24 */
25
26 /************************************************************************/
27 /* */
28 /* PROJECT : exFAT & FAT12/16/32 File System */
29 /* FILE : misc.c */
30 /* PURPOSE : Helper function for checksum and handing sdFAT error */
31 /* */
32 /*----------------------------------------------------------------------*/
33 /* NOTES */
34 /* */
35 /* */
36 /************************************************************************/
37
38 #include <linux/module.h>
39 #include <linux/fs.h>
40 #include <linux/buffer_head.h>
41 #include <linux/time.h>
42 #include "sdfat.h"
43 #include "version.h"
44
45 #ifdef CONFIG_SDFAT_SUPPORT_STLOG
46 #include <linux/stlog.h>
47 #else
48 #define ST_LOG(fmt, ...)
49 #endif
50
51 /*
52 * sdfat_fs_error reports a file system problem that might indicate fa data
53 * corruption/inconsistency. Depending on 'errors' mount option the
54 * panic() is called, or error message is printed FAT and nothing is done,
55 * or filesystem is remounted read-only (default behavior).
56 * In case the file system is remounted read-only, it can be made writable
57 * again by remounting it.
58 */
59 void __sdfat_fs_error(struct super_block *sb, int report, const char *fmt, ...)
60 {
61 struct sdfat_mount_options *opts = &SDFAT_SB(sb)->options;
62 va_list args;
63 struct va_format vaf;
64 struct block_device *bdev = sb->s_bdev;
65 dev_t bd_dev = bdev ? bdev->bd_dev : 0;
66
67 if (report) {
68 va_start(args, fmt);
69 vaf.fmt = fmt;
70 vaf.va = &args;
71 pr_err("[SDFAT](%s[%d:%d]):ERR: %pV\n",
72 sb->s_id, MAJOR(bd_dev), MINOR(bd_dev), &vaf);
73 #ifdef CONFIG_SDFAT_SUPPORT_STLOG
74 if (opts->errors == SDFAT_ERRORS_RO && !(sb->s_flags & MS_RDONLY)) {
75 ST_LOG("[SDFAT](%s[%d:%d]):ERR: %pV\n",
76 sb->s_id, MAJOR(bd_dev), MINOR(bd_dev), &vaf);
77 }
78 #endif
79 va_end(args);
80 }
81
82 if (opts->errors == SDFAT_ERRORS_PANIC) {
83 panic("[SDFAT](%s[%d:%d]): fs panic from previous error\n",
84 sb->s_id, MAJOR(bd_dev), MINOR(bd_dev));
85 } else if (opts->errors == SDFAT_ERRORS_RO && !(sb->s_flags & MS_RDONLY)) {
86 sb->s_flags |= MS_RDONLY;
87 pr_err("[SDFAT](%s[%d:%d]): Filesystem has been set "
88 "read-only\n", sb->s_id, MAJOR(bd_dev), MINOR(bd_dev));
89 #ifdef CONFIG_SDFAT_SUPPORT_STLOG
90 ST_LOG("[SDFAT](%s[%d:%d]): Filesystem has been set read-only\n",
91 sb->s_id, MAJOR(bd_dev), MINOR(bd_dev));
92 #endif
93 }
94 }
95 EXPORT_SYMBOL(__sdfat_fs_error);
96
97 /**
98 * __sdfat_msg() - print preformated SDFAT specific messages.
99 * All logs except what uses sdfat_fs_error() should be written by __sdfat_msg()
100 * If 'st' is set, the log is propagated to ST_LOG.
101 */
102 void __sdfat_msg(struct super_block *sb, const char *level, int st, const char *fmt, ...)
103 {
104 struct va_format vaf;
105 va_list args;
106 struct block_device *bdev = sb->s_bdev;
107 dev_t bd_dev = bdev ? bdev->bd_dev : 0;
108
109 va_start(args, fmt);
110 vaf.fmt = fmt;
111 vaf.va = &args;
112 /* level means KERN_ pacility level */
113 printk("%s[SDFAT](%s[%d:%d]): %pV\n", level,
114 sb->s_id, MAJOR(bd_dev), MINOR(bd_dev), &vaf);
115 #ifdef CONFIG_SDFAT_SUPPORT_STLOG
116 if (st) {
117 ST_LOG("[SDFAT](%s[%d:%d]): %pV\n",
118 sb->s_id, MAJOR(bd_dev), MINOR(bd_dev), &vaf);
119 }
120 #endif
121 va_end(args);
122 }
123 EXPORT_SYMBOL(__sdfat_msg);
124
125 void sdfat_log_version(void)
126 {
127 pr_info("[SDFAT] Filesystem version %s\n", SDFAT_VERSION);
128 #ifdef CONFIG_SDFAT_SUPPORT_STLOG
129 ST_LOG("[SDFAT] Filesystem version %s\n", SDFAT_VERSION);
130 #endif
131 }
132 EXPORT_SYMBOL(sdfat_log_version);
133
134 /* <linux/time.h> externs sys_tz
135 * extern struct timezone sys_tz;
136 */
137 #define UNIX_SECS_1980 315532800L
138
139 #if BITS_PER_LONG == 64
140 #define UNIX_SECS_2108 4354819200L
141 #endif
142
143 /* days between 1970/01/01 and 1980/01/01 (2 leap days) */
144 #define DAYS_DELTA_DECADE (365 * 10 + 2)
145 /* 120 (2100 - 1980) isn't leap year */
146 #define NO_LEAP_YEAR_2100 (120)
147 #define IS_LEAP_YEAR(y) (!((y) & 0x3) && (y) != NO_LEAP_YEAR_2100)
148
149 #define SECS_PER_MIN (60)
150 #define SECS_PER_HOUR (60 * SECS_PER_MIN)
151 #define SECS_PER_DAY (24 * SECS_PER_HOUR)
152
153 #define MAKE_LEAP_YEAR(leap_year, year) \
154 do { \
155 /* 2100 isn't leap year */ \
156 if (unlikely(year > NO_LEAP_YEAR_2100)) \
157 leap_year = ((year + 3) / 4) - 1; \
158 else \
159 leap_year = ((year + 3) / 4); \
160 } while (0)
161
162 /* Linear day numbers of the respective 1sts in non-leap years. */
163 static time_t accum_days_in_year[] = {
164 /* Month : N 01 02 03 04 05 06 07 08 09 10 11 12 */
165 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0,
166 };
167
168 /* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */
169 void sdfat_time_fat2unix(struct sdfat_sb_info *sbi, struct timespec *ts,
170 DATE_TIME_T *tp)
171 {
172 time_t year = tp->Year;
173 time_t ld; /* leap day */
174
175 MAKE_LEAP_YEAR(ld, year);
176
177 if (IS_LEAP_YEAR(year) && (tp->Month) > 2)
178 ld++;
179
180 ts->tv_sec = tp->Second + tp->Minute * SECS_PER_MIN
181 + tp->Hour * SECS_PER_HOUR
182 + (year * 365 + ld + accum_days_in_year[tp->Month]
183 + (tp->Day - 1) + DAYS_DELTA_DECADE) * SECS_PER_DAY;
184
185 if (!sbi->options.tz_utc)
186 ts->tv_sec += sys_tz.tz_minuteswest * SECS_PER_MIN;
187
188 ts->tv_nsec = 0;
189 }
190
191 /* Convert linear UNIX date to a FAT time/date pair. */
192 void sdfat_time_unix2fat(struct sdfat_sb_info *sbi, struct timespec *ts,
193 DATE_TIME_T *tp)
194 {
195 time_t second = ts->tv_sec;
196 time_t day, month, year;
197 time_t ld; /* leap day */
198
199 if (!sbi->options.tz_utc)
200 second -= sys_tz.tz_minuteswest * SECS_PER_MIN;
201
202 /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */
203 if (second < UNIX_SECS_1980) {
204 tp->Second = 0;
205 tp->Minute = 0;
206 tp->Hour = 0;
207 tp->Day = 1;
208 tp->Month = 1;
209 tp->Year = 0;
210 return;
211 }
212 #if (BITS_PER_LONG == 64)
213 if (second >= UNIX_SECS_2108) {
214 tp->Second = 59;
215 tp->Minute = 59;
216 tp->Hour = 23;
217 tp->Day = 31;
218 tp->Month = 12;
219 tp->Year = 127;
220 return;
221 }
222 #endif
223
224 day = second / SECS_PER_DAY - DAYS_DELTA_DECADE;
225 year = day / 365;
226
227 MAKE_LEAP_YEAR(ld, year);
228 if (year * 365 + ld > day)
229 year--;
230
231 MAKE_LEAP_YEAR(ld, year);
232 day -= year * 365 + ld;
233
234 if (IS_LEAP_YEAR(year) && day == accum_days_in_year[3]) {
235 month = 2;
236 } else {
237 if (IS_LEAP_YEAR(year) && day > accum_days_in_year[3])
238 day--;
239 for (month = 1; month < 12; month++) {
240 if (accum_days_in_year[month + 1] > day)
241 break;
242 }
243 }
244 day -= accum_days_in_year[month];
245
246 tp->Second = second % SECS_PER_MIN;
247 tp->Minute = (second / SECS_PER_MIN) % 60;
248 tp->Hour = (second / SECS_PER_HOUR) % 24;
249 tp->Day = day + 1;
250 tp->Month = month;
251 tp->Year = year;
252 }
253
254 TIMESTAMP_T *tm_now(struct sdfat_sb_info *sbi, TIMESTAMP_T *tp)
255 {
256 struct timespec ts = CURRENT_TIME_SEC;
257 DATE_TIME_T dt;
258
259 sdfat_time_unix2fat(sbi, &ts, &dt);
260
261 tp->year = dt.Year;
262 tp->mon = dt.Month;
263 tp->day = dt.Day;
264 tp->hour = dt.Hour;
265 tp->min = dt.Minute;
266 tp->sec = dt.Second;
267
268 return tp;
269 }
270
271 u8 calc_chksum_1byte(void *data, s32 len, u8 chksum)
272 {
273 s32 i;
274 u8 *c = (u8 *) data;
275
276 for (i = 0; i < len; i++, c++)
277 chksum = (((chksum & 1) << 7) | ((chksum & 0xFE) >> 1)) + *c;
278
279 return chksum;
280 }
281
282 u16 calc_chksum_2byte(void *data, s32 len, u16 chksum, s32 type)
283 {
284 s32 i;
285 u8 *c = (u8 *) data;
286
287 for (i = 0; i < len; i++, c++) {
288 if (((i == 2) || (i == 3)) && (type == CS_DIR_ENTRY))
289 continue;
290 chksum = (((chksum & 1) << 15) | ((chksum & 0xFFFE) >> 1)) + (u16) *c;
291 }
292 return chksum;
293 }
294
295 #ifdef CONFIG_SDFAT_TRACE_ELAPSED_TIME
296 struct timeval __t1, __t2;
297 u32 sdfat_time_current_usec(struct timeval *tv)
298 {
299 do_gettimeofday(tv);
300 return (u32)(tv->tv_sec*1000000 + tv->tv_usec);
301 }
302 #endif /* CONFIG_SDFAT_TRACE_ELAPSED_TIME */
303
304 #ifdef CONFIG_SDFAT_DBG_CAREFUL
305 /* Check the consistency of i_size_ondisk (FAT32, or flags 0x01 only) */
306 void sdfat_debug_check_clusters(struct inode *inode)
307 {
308 int num_clusters;
309 volatile uint32_t tmp_fat_chain[50];
310 volatile int num_clusters_org, tmp_i = 0;
311 CHAIN_T clu;
312 FILE_ID_T *fid = &(SDFAT_I(inode)->fid);
313 FS_INFO_T *fsi = &(SDFAT_SB(inode->i_sb)->fsi);
314
315 if (SDFAT_I(inode)->i_size_ondisk == 0)
316 num_clusters = 0;
317 else
318 num_clusters = (s32)((SDFAT_I(inode)->i_size_ondisk-1) >> fsi->cluster_size_bits) + 1;
319
320 clu.dir = fid->start_clu;
321 clu.size = num_clusters;
322 clu.flags = fid->flags;
323
324 num_clusters_org = num_clusters;
325
326 if (clu.flags == 0x03)
327 return;
328
329 while (num_clusters > 0) {
330 /* FAT chain logging */
331 tmp_fat_chain[tmp_i] = clu.dir;
332 tmp_i++;
333 if (tmp_i >= 50)
334 tmp_i = 0;
335
336 BUG_ON(IS_CLUS_EOF(clu.dir) || IS_CLUS_FREE(clu.dir));
337
338 if (get_next_clus_safe(inode->i_sb, &(clu.dir)))
339 EMSG("%s: failed to access to FAT\n");
340
341 num_clusters--;
342 }
343
344 BUG_ON(!IS_CLUS_EOF(clu.dir));
345 }
346
347 #endif /* CONFIG_SDFAT_DBG_CAREFUL */
348
349 #ifdef CONFIG_SDFAT_DBG_MSG
350 void __sdfat_dmsg(int level, const char *fmt, ...)
351 {
352 #ifdef CONFIG_SDFAT_DBG_SHOW_PID
353 struct va_format vaf;
354 va_list args;
355
356 /* should check type */
357 if (level > SDFAT_MSG_LEVEL)
358 return;
359
360 va_start(args, fmt);
361 vaf.fmt = fmt;
362 vaf.va = &args;
363 /* fmt already includes KERN_ pacility level */
364 printk("[%u] %pV", current->pid, &vaf);
365 va_end(args);
366 #else
367 va_list args;
368
369 /* should check type */
370 if (level > SDFAT_MSG_LEVEL)
371 return;
372
373 va_start(args, fmt);
374 /* fmt already includes KERN_ pacility level */
375 vprintk(fmt, args);
376 va_end(args);
377 #endif
378 }
379 #endif
380