xref: /openbmc/linux/fs/exfat/misc.c (revision 6425baab)
1772b29ccSNamjae Jeon // SPDX-License-Identifier: GPL-2.0-or-later
2772b29ccSNamjae Jeon /*
3772b29ccSNamjae Jeon  *  Written 1992,1993 by Werner Almesberger
4772b29ccSNamjae Jeon  *  22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980
5772b29ccSNamjae Jeon  *		 and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru)
6772b29ccSNamjae Jeon  * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
7772b29ccSNamjae Jeon  */
8772b29ccSNamjae Jeon 
9772b29ccSNamjae Jeon #include <linux/time.h>
10772b29ccSNamjae Jeon #include <linux/fs.h>
11772b29ccSNamjae Jeon #include <linux/slab.h>
12772b29ccSNamjae Jeon #include <linux/buffer_head.h>
133d966521SYuezhang.Mo #include <linux/blk_types.h>
14772b29ccSNamjae Jeon 
15772b29ccSNamjae Jeon #include "exfat_raw.h"
16772b29ccSNamjae Jeon #include "exfat_fs.h"
17772b29ccSNamjae Jeon 
18772b29ccSNamjae Jeon /*
19772b29ccSNamjae Jeon  * exfat_fs_error reports a file system problem that might indicate fa data
20772b29ccSNamjae Jeon  * corruption/inconsistency. Depending on 'errors' mount option the
21772b29ccSNamjae Jeon  * panic() is called, or error message is printed FAT and nothing is done,
22772b29ccSNamjae Jeon  * or filesystem is remounted read-only (default behavior).
23772b29ccSNamjae Jeon  * In case the file system is remounted read-only, it can be made writable
24772b29ccSNamjae Jeon  * again by remounting it.
25772b29ccSNamjae Jeon  */
__exfat_fs_error(struct super_block * sb,int report,const char * fmt,...)26772b29ccSNamjae Jeon void __exfat_fs_error(struct super_block *sb, int report, const char *fmt, ...)
27772b29ccSNamjae Jeon {
28772b29ccSNamjae Jeon 	struct exfat_mount_options *opts = &EXFAT_SB(sb)->options;
29772b29ccSNamjae Jeon 	va_list args;
30772b29ccSNamjae Jeon 	struct va_format vaf;
31772b29ccSNamjae Jeon 
32772b29ccSNamjae Jeon 	if (report) {
33772b29ccSNamjae Jeon 		va_start(args, fmt);
34772b29ccSNamjae Jeon 		vaf.fmt = fmt;
35772b29ccSNamjae Jeon 		vaf.va = &args;
36d1727d55SJoe Perches 		exfat_err(sb, "error, %pV", &vaf);
37772b29ccSNamjae Jeon 		va_end(args);
38772b29ccSNamjae Jeon 	}
39772b29ccSNamjae Jeon 
40772b29ccSNamjae Jeon 	if (opts->errors == EXFAT_ERRORS_PANIC) {
41772b29ccSNamjae Jeon 		panic("exFAT-fs (%s): fs panic from previous error\n",
42772b29ccSNamjae Jeon 			sb->s_id);
43772b29ccSNamjae Jeon 	} else if (opts->errors == EXFAT_ERRORS_RO && !sb_rdonly(sb)) {
44772b29ccSNamjae Jeon 		sb->s_flags |= SB_RDONLY;
45d1727d55SJoe Perches 		exfat_err(sb, "Filesystem has been set read-only");
46772b29ccSNamjae Jeon 	}
47772b29ccSNamjae Jeon }
48772b29ccSNamjae Jeon 
49772b29ccSNamjae Jeon #define SECS_PER_MIN    (60)
50772b29ccSNamjae Jeon #define TIMEZONE_SEC(x)	((x) * 15 * SECS_PER_MIN)
51772b29ccSNamjae Jeon 
exfat_adjust_tz(struct timespec64 * ts,u8 tz_off)52772b29ccSNamjae Jeon static void exfat_adjust_tz(struct timespec64 *ts, u8 tz_off)
53772b29ccSNamjae Jeon {
54772b29ccSNamjae Jeon 	if (tz_off <= 0x3F)
55772b29ccSNamjae Jeon 		ts->tv_sec -= TIMEZONE_SEC(tz_off);
56772b29ccSNamjae Jeon 	else /* 0x40 <= (tz_off & 0x7F) <=0x7F */
57772b29ccSNamjae Jeon 		ts->tv_sec += TIMEZONE_SEC(0x80 - tz_off);
58772b29ccSNamjae Jeon }
59772b29ccSNamjae Jeon 
exfat_tz_offset(struct exfat_sb_info * sbi)60*9b002894SChung-Chiang Cheng static inline int exfat_tz_offset(struct exfat_sb_info *sbi)
61*9b002894SChung-Chiang Cheng {
62*9b002894SChung-Chiang Cheng 	if (sbi->options.sys_tz)
63*9b002894SChung-Chiang Cheng 		return -sys_tz.tz_minuteswest;
64*9b002894SChung-Chiang Cheng 	return sbi->options.time_offset;
65*9b002894SChung-Chiang Cheng }
66*9b002894SChung-Chiang Cheng 
67772b29ccSNamjae Jeon /* 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)68772b29ccSNamjae Jeon void exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
69ed0f84d3STetsuhiro Kohada 		u8 tz, __le16 time, __le16 date, u8 time_cs)
70772b29ccSNamjae Jeon {
71772b29ccSNamjae Jeon 	u16 t = le16_to_cpu(time);
72772b29ccSNamjae Jeon 	u16 d = le16_to_cpu(date);
73772b29ccSNamjae Jeon 
74772b29ccSNamjae Jeon 	ts->tv_sec = mktime64(1980 + (d >> 9), d >> 5 & 0x000F, d & 0x001F,
75772b29ccSNamjae Jeon 			      t >> 11, (t >> 5) & 0x003F, (t & 0x001F) << 1);
76772b29ccSNamjae Jeon 
77772b29ccSNamjae Jeon 
78ed0f84d3STetsuhiro Kohada 	/* time_cs field represent 0 ~ 199cs(1990 ms) */
79ed0f84d3STetsuhiro Kohada 	if (time_cs) {
80ed0f84d3STetsuhiro Kohada 		ts->tv_sec += time_cs / 100;
81ed0f84d3STetsuhiro Kohada 		ts->tv_nsec = (time_cs % 100) * 10 * NSEC_PER_MSEC;
8281df1ad4SEric Sandeen 	} else
8381df1ad4SEric Sandeen 		ts->tv_nsec = 0;
84772b29ccSNamjae Jeon 
85772b29ccSNamjae Jeon 	if (tz & EXFAT_TZ_VALID)
86772b29ccSNamjae Jeon 		/* Adjust timezone to UTC0. */
87772b29ccSNamjae Jeon 		exfat_adjust_tz(ts, tz & ~EXFAT_TZ_VALID);
88772b29ccSNamjae Jeon 	else
89*9b002894SChung-Chiang Cheng 		ts->tv_sec -= exfat_tz_offset(sbi) * SECS_PER_MIN;
90772b29ccSNamjae Jeon }
91772b29ccSNamjae Jeon 
92772b29ccSNamjae Jeon /* 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)93772b29ccSNamjae Jeon void exfat_set_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
94ed0f84d3STetsuhiro Kohada 		u8 *tz, __le16 *time, __le16 *date, u8 *time_cs)
95772b29ccSNamjae Jeon {
96772b29ccSNamjae Jeon 	struct tm tm;
97772b29ccSNamjae Jeon 	u16 t, d;
98772b29ccSNamjae Jeon 
99772b29ccSNamjae Jeon 	time64_to_tm(ts->tv_sec, 0, &tm);
100772b29ccSNamjae Jeon 	t = (tm.tm_hour << 11) | (tm.tm_min << 5) | (tm.tm_sec >> 1);
101772b29ccSNamjae Jeon 	d = ((tm.tm_year - 80) <<  9) | ((tm.tm_mon + 1) << 5) | tm.tm_mday;
102772b29ccSNamjae Jeon 
103772b29ccSNamjae Jeon 	*time = cpu_to_le16(t);
104772b29ccSNamjae Jeon 	*date = cpu_to_le16(d);
105772b29ccSNamjae Jeon 
106ed0f84d3STetsuhiro Kohada 	/* time_cs field represent 0 ~ 199cs(1990 ms) */
107ed0f84d3STetsuhiro Kohada 	if (time_cs)
108ed0f84d3STetsuhiro Kohada 		*time_cs = (tm.tm_sec & 1) * 100 +
109772b29ccSNamjae Jeon 			ts->tv_nsec / (10 * NSEC_PER_MSEC);
110772b29ccSNamjae Jeon 
111772b29ccSNamjae Jeon 	/*
112772b29ccSNamjae Jeon 	 * Record 00h value for OffsetFromUtc field and 1 value for OffsetValid
113772b29ccSNamjae Jeon 	 * to indicate that local time and UTC are the same.
114772b29ccSNamjae Jeon 	 */
115772b29ccSNamjae Jeon 	*tz = EXFAT_TZ_VALID;
116772b29ccSNamjae Jeon }
117772b29ccSNamjae Jeon 
11881df1ad4SEric Sandeen /*
11981df1ad4SEric Sandeen  * The timestamp for access_time has double seconds granularity.
12081df1ad4SEric Sandeen  * (There is no 10msIncrement field for access_time unlike create/modify_time)
12181df1ad4SEric Sandeen  * atime also has only a 2-second resolution.
12281df1ad4SEric Sandeen  */
exfat_truncate_atime(struct timespec64 * ts)12381df1ad4SEric Sandeen void exfat_truncate_atime(struct timespec64 *ts)
12481df1ad4SEric Sandeen {
12581df1ad4SEric Sandeen 	ts->tv_sec = round_down(ts->tv_sec, 2);
12681df1ad4SEric Sandeen 	ts->tv_nsec = 0;
12781df1ad4SEric Sandeen }
12881df1ad4SEric Sandeen 
exfat_calc_chksum16(void * data,int len,u16 chksum,int type)1295875bf28STetsuhiro Kohada u16 exfat_calc_chksum16(void *data, int len, u16 chksum, int type)
130772b29ccSNamjae Jeon {
131772b29ccSNamjae Jeon 	int i;
1325875bf28STetsuhiro Kohada 	u8 *c = (u8 *)data;
133772b29ccSNamjae Jeon 
134772b29ccSNamjae Jeon 	for (i = 0; i < len; i++, c++) {
1355875bf28STetsuhiro Kohada 		if (unlikely(type == CS_DIR_ENTRY && (i == 2 || i == 3)))
136772b29ccSNamjae Jeon 			continue;
1375875bf28STetsuhiro Kohada 		chksum = ((chksum << 15) | (chksum >> 1)) + *c;
138772b29ccSNamjae Jeon 	}
139772b29ccSNamjae Jeon 	return chksum;
140772b29ccSNamjae Jeon }
141772b29ccSNamjae Jeon 
exfat_calc_chksum32(void * data,int len,u32 chksum,int type)142476189c0STetsuhiro Kohada u32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type)
143476189c0STetsuhiro Kohada {
144476189c0STetsuhiro Kohada 	int i;
145476189c0STetsuhiro Kohada 	u8 *c = (u8 *)data;
146476189c0STetsuhiro Kohada 
147476189c0STetsuhiro Kohada 	for (i = 0; i < len; i++, c++) {
148476189c0STetsuhiro Kohada 		if (unlikely(type == CS_BOOT_SECTOR &&
149476189c0STetsuhiro Kohada 			     (i == 106 || i == 107 || i == 112)))
150476189c0STetsuhiro Kohada 			continue;
151476189c0STetsuhiro Kohada 		chksum = ((chksum << 31) | (chksum >> 1)) + *c;
152476189c0STetsuhiro Kohada 	}
153476189c0STetsuhiro Kohada 	return chksum;
154476189c0STetsuhiro Kohada }
155476189c0STetsuhiro Kohada 
exfat_update_bh(struct buffer_head * bh,int sync)1562c7f8937STetsuhiro Kohada void exfat_update_bh(struct buffer_head *bh, int sync)
157772b29ccSNamjae Jeon {
158772b29ccSNamjae Jeon 	set_buffer_uptodate(bh);
159772b29ccSNamjae Jeon 	mark_buffer_dirty(bh);
160772b29ccSNamjae Jeon 
161772b29ccSNamjae Jeon 	if (sync)
162772b29ccSNamjae Jeon 		sync_dirty_buffer(bh);
163772b29ccSNamjae Jeon }
164772b29ccSNamjae Jeon 
exfat_update_bhs(struct buffer_head ** bhs,int nr_bhs,int sync)1653db3c3fbSTetsuhiro Kohada int exfat_update_bhs(struct buffer_head **bhs, int nr_bhs, int sync)
1663db3c3fbSTetsuhiro Kohada {
1673db3c3fbSTetsuhiro Kohada 	int i, err = 0;
1683db3c3fbSTetsuhiro Kohada 
1693db3c3fbSTetsuhiro Kohada 	for (i = 0; i < nr_bhs; i++) {
1703db3c3fbSTetsuhiro Kohada 		set_buffer_uptodate(bhs[i]);
1713db3c3fbSTetsuhiro Kohada 		mark_buffer_dirty(bhs[i]);
1723db3c3fbSTetsuhiro Kohada 		if (sync)
1733d966521SYuezhang.Mo 			write_dirty_buffer(bhs[i], REQ_SYNC);
1743db3c3fbSTetsuhiro Kohada 	}
1753db3c3fbSTetsuhiro Kohada 
1763db3c3fbSTetsuhiro Kohada 	for (i = 0; i < nr_bhs && sync; i++) {
1773db3c3fbSTetsuhiro Kohada 		wait_on_buffer(bhs[i]);
1783db3c3fbSTetsuhiro Kohada 		if (!err && !buffer_uptodate(bhs[i]))
1793db3c3fbSTetsuhiro Kohada 			err = -EIO;
1803db3c3fbSTetsuhiro Kohada 	}
1813db3c3fbSTetsuhiro Kohada 	return err;
1823db3c3fbSTetsuhiro Kohada }
1833db3c3fbSTetsuhiro Kohada 
exfat_chain_set(struct exfat_chain * ec,unsigned int dir,unsigned int size,unsigned char flags)184772b29ccSNamjae Jeon void exfat_chain_set(struct exfat_chain *ec, unsigned int dir,
185772b29ccSNamjae Jeon 		unsigned int size, unsigned char flags)
186772b29ccSNamjae Jeon {
187772b29ccSNamjae Jeon 	ec->dir = dir;
188772b29ccSNamjae Jeon 	ec->size = size;
189772b29ccSNamjae Jeon 	ec->flags = flags;
190772b29ccSNamjae Jeon }
191772b29ccSNamjae Jeon 
exfat_chain_dup(struct exfat_chain * dup,struct exfat_chain * ec)192772b29ccSNamjae Jeon void exfat_chain_dup(struct exfat_chain *dup, struct exfat_chain *ec)
193772b29ccSNamjae Jeon {
194772b29ccSNamjae Jeon 	return exfat_chain_set(dup, ec->dir, ec->size, ec->flags);
195772b29ccSNamjae Jeon }
196