xref: /OK3568_Linux_fs/kernel/fs/exfat/misc.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *  Written 1992,1993 by Werner Almesberger
4*4882a593Smuzhiyun  *  22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980
5*4882a593Smuzhiyun  *		 and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru)
6*4882a593Smuzhiyun  * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
7*4882a593Smuzhiyun  */
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun #include <linux/time.h>
10*4882a593Smuzhiyun #include <linux/fs.h>
11*4882a593Smuzhiyun #include <linux/slab.h>
12*4882a593Smuzhiyun #include <linux/buffer_head.h>
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #include "exfat_raw.h"
15*4882a593Smuzhiyun #include "exfat_fs.h"
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun /*
18*4882a593Smuzhiyun  * exfat_fs_error reports a file system problem that might indicate fa data
19*4882a593Smuzhiyun  * corruption/inconsistency. Depending on 'errors' mount option the
20*4882a593Smuzhiyun  * panic() is called, or error message is printed FAT and nothing is done,
21*4882a593Smuzhiyun  * or filesystem is remounted read-only (default behavior).
22*4882a593Smuzhiyun  * In case the file system is remounted read-only, it can be made writable
23*4882a593Smuzhiyun  * again by remounting it.
24*4882a593Smuzhiyun  */
__exfat_fs_error(struct super_block * sb,int report,const char * fmt,...)25*4882a593Smuzhiyun void __exfat_fs_error(struct super_block *sb, int report, const char *fmt, ...)
26*4882a593Smuzhiyun {
27*4882a593Smuzhiyun 	struct exfat_mount_options *opts = &EXFAT_SB(sb)->options;
28*4882a593Smuzhiyun 	va_list args;
29*4882a593Smuzhiyun 	struct va_format vaf;
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun 	if (report) {
32*4882a593Smuzhiyun 		va_start(args, fmt);
33*4882a593Smuzhiyun 		vaf.fmt = fmt;
34*4882a593Smuzhiyun 		vaf.va = &args;
35*4882a593Smuzhiyun 		exfat_err(sb, "error, %pV", &vaf);
36*4882a593Smuzhiyun 		va_end(args);
37*4882a593Smuzhiyun 	}
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun 	if (opts->errors == EXFAT_ERRORS_PANIC) {
40*4882a593Smuzhiyun 		panic("exFAT-fs (%s): fs panic from previous error\n",
41*4882a593Smuzhiyun 			sb->s_id);
42*4882a593Smuzhiyun 	} else if (opts->errors == EXFAT_ERRORS_RO && !sb_rdonly(sb)) {
43*4882a593Smuzhiyun 		sb->s_flags |= SB_RDONLY;
44*4882a593Smuzhiyun 		exfat_err(sb, "Filesystem has been set read-only");
45*4882a593Smuzhiyun 	}
46*4882a593Smuzhiyun }
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun /*
49*4882a593Smuzhiyun  * exfat_msg() - print preformated EXFAT specific messages.
50*4882a593Smuzhiyun  * All logs except what uses exfat_fs_error() should be written by exfat_msg()
51*4882a593Smuzhiyun  */
exfat_msg(struct super_block * sb,const char * level,const char * fmt,...)52*4882a593Smuzhiyun void exfat_msg(struct super_block *sb, const char *level, const char *fmt, ...)
53*4882a593Smuzhiyun {
54*4882a593Smuzhiyun 	struct va_format vaf;
55*4882a593Smuzhiyun 	va_list args;
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun 	va_start(args, fmt);
58*4882a593Smuzhiyun 	vaf.fmt = fmt;
59*4882a593Smuzhiyun 	vaf.va = &args;
60*4882a593Smuzhiyun 	/* level means KERN_ pacility level */
61*4882a593Smuzhiyun 	printk("%sexFAT-fs (%s): %pV\n", level, sb->s_id, &vaf);
62*4882a593Smuzhiyun 	va_end(args);
63*4882a593Smuzhiyun }
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun #define SECS_PER_MIN    (60)
66*4882a593Smuzhiyun #define TIMEZONE_SEC(x)	((x) * 15 * SECS_PER_MIN)
67*4882a593Smuzhiyun 
exfat_adjust_tz(struct timespec64 * ts,u8 tz_off)68*4882a593Smuzhiyun static void exfat_adjust_tz(struct timespec64 *ts, u8 tz_off)
69*4882a593Smuzhiyun {
70*4882a593Smuzhiyun 	if (tz_off <= 0x3F)
71*4882a593Smuzhiyun 		ts->tv_sec -= TIMEZONE_SEC(tz_off);
72*4882a593Smuzhiyun 	else /* 0x40 <= (tz_off & 0x7F) <=0x7F */
73*4882a593Smuzhiyun 		ts->tv_sec += TIMEZONE_SEC(0x80 - tz_off);
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun /* Convert a EXFAT time/date pair to a UNIX date (seconds since 1 1 70). */
exfat_get_entry_time(struct exfat_sb_info * sbi,struct timespec64 * ts,u8 tz,__le16 time,__le16 date,u8 time_cs)77*4882a593Smuzhiyun void exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
78*4882a593Smuzhiyun 		u8 tz, __le16 time, __le16 date, u8 time_cs)
79*4882a593Smuzhiyun {
80*4882a593Smuzhiyun 	u16 t = le16_to_cpu(time);
81*4882a593Smuzhiyun 	u16 d = le16_to_cpu(date);
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun 	ts->tv_sec = mktime64(1980 + (d >> 9), d >> 5 & 0x000F, d & 0x001F,
84*4882a593Smuzhiyun 			      t >> 11, (t >> 5) & 0x003F, (t & 0x001F) << 1);
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun 	/* time_cs field represent 0 ~ 199cs(1990 ms) */
88*4882a593Smuzhiyun 	if (time_cs) {
89*4882a593Smuzhiyun 		ts->tv_sec += time_cs / 100;
90*4882a593Smuzhiyun 		ts->tv_nsec = (time_cs % 100) * 10 * NSEC_PER_MSEC;
91*4882a593Smuzhiyun 	} else
92*4882a593Smuzhiyun 		ts->tv_nsec = 0;
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 	if (tz & EXFAT_TZ_VALID)
95*4882a593Smuzhiyun 		/* Adjust timezone to UTC0. */
96*4882a593Smuzhiyun 		exfat_adjust_tz(ts, tz & ~EXFAT_TZ_VALID);
97*4882a593Smuzhiyun 	else
98*4882a593Smuzhiyun 		/* Convert from local time to UTC using time_offset. */
99*4882a593Smuzhiyun 		ts->tv_sec -= sbi->options.time_offset * SECS_PER_MIN;
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun /* Convert linear UNIX date to a EXFAT time/date pair. */
exfat_set_entry_time(struct exfat_sb_info * sbi,struct timespec64 * ts,u8 * tz,__le16 * time,__le16 * date,u8 * time_cs)103*4882a593Smuzhiyun void exfat_set_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
104*4882a593Smuzhiyun 		u8 *tz, __le16 *time, __le16 *date, u8 *time_cs)
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun 	struct tm tm;
107*4882a593Smuzhiyun 	u16 t, d;
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	time64_to_tm(ts->tv_sec, 0, &tm);
110*4882a593Smuzhiyun 	t = (tm.tm_hour << 11) | (tm.tm_min << 5) | (tm.tm_sec >> 1);
111*4882a593Smuzhiyun 	d = ((tm.tm_year - 80) <<  9) | ((tm.tm_mon + 1) << 5) | tm.tm_mday;
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun 	*time = cpu_to_le16(t);
114*4882a593Smuzhiyun 	*date = cpu_to_le16(d);
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 	/* time_cs field represent 0 ~ 199cs(1990 ms) */
117*4882a593Smuzhiyun 	if (time_cs)
118*4882a593Smuzhiyun 		*time_cs = (tm.tm_sec & 1) * 100 +
119*4882a593Smuzhiyun 			ts->tv_nsec / (10 * NSEC_PER_MSEC);
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 	/*
122*4882a593Smuzhiyun 	 * Record 00h value for OffsetFromUtc field and 1 value for OffsetValid
123*4882a593Smuzhiyun 	 * to indicate that local time and UTC are the same.
124*4882a593Smuzhiyun 	 */
125*4882a593Smuzhiyun 	*tz = EXFAT_TZ_VALID;
126*4882a593Smuzhiyun }
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun /*
129*4882a593Smuzhiyun  * The timestamp for access_time has double seconds granularity.
130*4882a593Smuzhiyun  * (There is no 10msIncrement field for access_time unlike create/modify_time)
131*4882a593Smuzhiyun  * atime also has only a 2-second resolution.
132*4882a593Smuzhiyun  */
exfat_truncate_atime(struct timespec64 * ts)133*4882a593Smuzhiyun void exfat_truncate_atime(struct timespec64 *ts)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun 	ts->tv_sec = round_down(ts->tv_sec, 2);
136*4882a593Smuzhiyun 	ts->tv_nsec = 0;
137*4882a593Smuzhiyun }
138*4882a593Smuzhiyun 
exfat_calc_chksum16(void * data,int len,u16 chksum,int type)139*4882a593Smuzhiyun u16 exfat_calc_chksum16(void *data, int len, u16 chksum, int type)
140*4882a593Smuzhiyun {
141*4882a593Smuzhiyun 	int i;
142*4882a593Smuzhiyun 	u8 *c = (u8 *)data;
143*4882a593Smuzhiyun 
144*4882a593Smuzhiyun 	for (i = 0; i < len; i++, c++) {
145*4882a593Smuzhiyun 		if (unlikely(type == CS_DIR_ENTRY && (i == 2 || i == 3)))
146*4882a593Smuzhiyun 			continue;
147*4882a593Smuzhiyun 		chksum = ((chksum << 15) | (chksum >> 1)) + *c;
148*4882a593Smuzhiyun 	}
149*4882a593Smuzhiyun 	return chksum;
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun 
exfat_calc_chksum32(void * data,int len,u32 chksum,int type)152*4882a593Smuzhiyun u32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type)
153*4882a593Smuzhiyun {
154*4882a593Smuzhiyun 	int i;
155*4882a593Smuzhiyun 	u8 *c = (u8 *)data;
156*4882a593Smuzhiyun 
157*4882a593Smuzhiyun 	for (i = 0; i < len; i++, c++) {
158*4882a593Smuzhiyun 		if (unlikely(type == CS_BOOT_SECTOR &&
159*4882a593Smuzhiyun 			     (i == 106 || i == 107 || i == 112)))
160*4882a593Smuzhiyun 			continue;
161*4882a593Smuzhiyun 		chksum = ((chksum << 31) | (chksum >> 1)) + *c;
162*4882a593Smuzhiyun 	}
163*4882a593Smuzhiyun 	return chksum;
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun 
exfat_update_bh(struct buffer_head * bh,int sync)166*4882a593Smuzhiyun void exfat_update_bh(struct buffer_head *bh, int sync)
167*4882a593Smuzhiyun {
168*4882a593Smuzhiyun 	set_buffer_uptodate(bh);
169*4882a593Smuzhiyun 	mark_buffer_dirty(bh);
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 	if (sync)
172*4882a593Smuzhiyun 		sync_dirty_buffer(bh);
173*4882a593Smuzhiyun }
174*4882a593Smuzhiyun 
exfat_update_bhs(struct buffer_head ** bhs,int nr_bhs,int sync)175*4882a593Smuzhiyun int exfat_update_bhs(struct buffer_head **bhs, int nr_bhs, int sync)
176*4882a593Smuzhiyun {
177*4882a593Smuzhiyun 	int i, err = 0;
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun 	for (i = 0; i < nr_bhs; i++) {
180*4882a593Smuzhiyun 		set_buffer_uptodate(bhs[i]);
181*4882a593Smuzhiyun 		mark_buffer_dirty(bhs[i]);
182*4882a593Smuzhiyun 		if (sync)
183*4882a593Smuzhiyun 			write_dirty_buffer(bhs[i], 0);
184*4882a593Smuzhiyun 	}
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	for (i = 0; i < nr_bhs && sync; i++) {
187*4882a593Smuzhiyun 		wait_on_buffer(bhs[i]);
188*4882a593Smuzhiyun 		if (!err && !buffer_uptodate(bhs[i]))
189*4882a593Smuzhiyun 			err = -EIO;
190*4882a593Smuzhiyun 	}
191*4882a593Smuzhiyun 	return err;
192*4882a593Smuzhiyun }
193*4882a593Smuzhiyun 
exfat_chain_set(struct exfat_chain * ec,unsigned int dir,unsigned int size,unsigned char flags)194*4882a593Smuzhiyun void exfat_chain_set(struct exfat_chain *ec, unsigned int dir,
195*4882a593Smuzhiyun 		unsigned int size, unsigned char flags)
196*4882a593Smuzhiyun {
197*4882a593Smuzhiyun 	ec->dir = dir;
198*4882a593Smuzhiyun 	ec->size = size;
199*4882a593Smuzhiyun 	ec->flags = flags;
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun 
exfat_chain_dup(struct exfat_chain * dup,struct exfat_chain * ec)202*4882a593Smuzhiyun void exfat_chain_dup(struct exfat_chain *dup, struct exfat_chain *ec)
203*4882a593Smuzhiyun {
204*4882a593Smuzhiyun 	return exfat_chain_set(dup, ec->dir, ec->size, ec->flags);
205*4882a593Smuzhiyun }
206