18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Written 1992,1993 by Werner Almesberger 48c2ecf20Sopenharmony_ci * 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980 58c2ecf20Sopenharmony_ci * and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru) 68c2ecf20Sopenharmony_ci * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/time.h> 108c2ecf20Sopenharmony_ci#include <linux/fs.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/buffer_head.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "exfat_raw.h" 158c2ecf20Sopenharmony_ci#include "exfat_fs.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* 188c2ecf20Sopenharmony_ci * exfat_fs_error reports a file system problem that might indicate fa data 198c2ecf20Sopenharmony_ci * corruption/inconsistency. Depending on 'errors' mount option the 208c2ecf20Sopenharmony_ci * panic() is called, or error message is printed FAT and nothing is done, 218c2ecf20Sopenharmony_ci * or filesystem is remounted read-only (default behavior). 228c2ecf20Sopenharmony_ci * In case the file system is remounted read-only, it can be made writable 238c2ecf20Sopenharmony_ci * again by remounting it. 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_civoid __exfat_fs_error(struct super_block *sb, int report, const char *fmt, ...) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci struct exfat_mount_options *opts = &EXFAT_SB(sb)->options; 288c2ecf20Sopenharmony_ci va_list args; 298c2ecf20Sopenharmony_ci struct va_format vaf; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci if (report) { 328c2ecf20Sopenharmony_ci va_start(args, fmt); 338c2ecf20Sopenharmony_ci vaf.fmt = fmt; 348c2ecf20Sopenharmony_ci vaf.va = &args; 358c2ecf20Sopenharmony_ci exfat_err(sb, "error, %pV", &vaf); 368c2ecf20Sopenharmony_ci va_end(args); 378c2ecf20Sopenharmony_ci } 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci if (opts->errors == EXFAT_ERRORS_PANIC) { 408c2ecf20Sopenharmony_ci panic("exFAT-fs (%s): fs panic from previous error\n", 418c2ecf20Sopenharmony_ci sb->s_id); 428c2ecf20Sopenharmony_ci } else if (opts->errors == EXFAT_ERRORS_RO && !sb_rdonly(sb)) { 438c2ecf20Sopenharmony_ci sb->s_flags |= SB_RDONLY; 448c2ecf20Sopenharmony_ci exfat_err(sb, "Filesystem has been set read-only"); 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* 498c2ecf20Sopenharmony_ci * exfat_msg() - print preformated EXFAT specific messages. 508c2ecf20Sopenharmony_ci * All logs except what uses exfat_fs_error() should be written by exfat_msg() 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_civoid exfat_msg(struct super_block *sb, const char *level, const char *fmt, ...) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct va_format vaf; 558c2ecf20Sopenharmony_ci va_list args; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci va_start(args, fmt); 588c2ecf20Sopenharmony_ci vaf.fmt = fmt; 598c2ecf20Sopenharmony_ci vaf.va = &args; 608c2ecf20Sopenharmony_ci /* level means KERN_ pacility level */ 618c2ecf20Sopenharmony_ci printk("%sexFAT-fs (%s): %pV\n", level, sb->s_id, &vaf); 628c2ecf20Sopenharmony_ci va_end(args); 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define SECS_PER_MIN (60) 668c2ecf20Sopenharmony_ci#define TIMEZONE_SEC(x) ((x) * 15 * SECS_PER_MIN) 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic void exfat_adjust_tz(struct timespec64 *ts, u8 tz_off) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci if (tz_off <= 0x3F) 718c2ecf20Sopenharmony_ci ts->tv_sec -= TIMEZONE_SEC(tz_off); 728c2ecf20Sopenharmony_ci else /* 0x40 <= (tz_off & 0x7F) <=0x7F */ 738c2ecf20Sopenharmony_ci ts->tv_sec += TIMEZONE_SEC(0x80 - tz_off); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* Convert a EXFAT time/date pair to a UNIX date (seconds since 1 1 70). */ 778c2ecf20Sopenharmony_civoid exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts, 788c2ecf20Sopenharmony_ci u8 tz, __le16 time, __le16 date, u8 time_cs) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci u16 t = le16_to_cpu(time); 818c2ecf20Sopenharmony_ci u16 d = le16_to_cpu(date); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci ts->tv_sec = mktime64(1980 + (d >> 9), d >> 5 & 0x000F, d & 0x001F, 848c2ecf20Sopenharmony_ci t >> 11, (t >> 5) & 0x003F, (t & 0x001F) << 1); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci /* time_cs field represent 0 ~ 199cs(1990 ms) */ 888c2ecf20Sopenharmony_ci if (time_cs) { 898c2ecf20Sopenharmony_ci ts->tv_sec += time_cs / 100; 908c2ecf20Sopenharmony_ci ts->tv_nsec = (time_cs % 100) * 10 * NSEC_PER_MSEC; 918c2ecf20Sopenharmony_ci } else 928c2ecf20Sopenharmony_ci ts->tv_nsec = 0; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (tz & EXFAT_TZ_VALID) 958c2ecf20Sopenharmony_ci /* Adjust timezone to UTC0. */ 968c2ecf20Sopenharmony_ci exfat_adjust_tz(ts, tz & ~EXFAT_TZ_VALID); 978c2ecf20Sopenharmony_ci else 988c2ecf20Sopenharmony_ci /* Convert from local time to UTC using time_offset. */ 998c2ecf20Sopenharmony_ci ts->tv_sec -= sbi->options.time_offset * SECS_PER_MIN; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/* Convert linear UNIX date to a EXFAT time/date pair. */ 1038c2ecf20Sopenharmony_civoid exfat_set_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts, 1048c2ecf20Sopenharmony_ci u8 *tz, __le16 *time, __le16 *date, u8 *time_cs) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct tm tm; 1078c2ecf20Sopenharmony_ci u16 t, d; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci time64_to_tm(ts->tv_sec, 0, &tm); 1108c2ecf20Sopenharmony_ci t = (tm.tm_hour << 11) | (tm.tm_min << 5) | (tm.tm_sec >> 1); 1118c2ecf20Sopenharmony_ci d = ((tm.tm_year - 80) << 9) | ((tm.tm_mon + 1) << 5) | tm.tm_mday; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci *time = cpu_to_le16(t); 1148c2ecf20Sopenharmony_ci *date = cpu_to_le16(d); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* time_cs field represent 0 ~ 199cs(1990 ms) */ 1178c2ecf20Sopenharmony_ci if (time_cs) 1188c2ecf20Sopenharmony_ci *time_cs = (tm.tm_sec & 1) * 100 + 1198c2ecf20Sopenharmony_ci ts->tv_nsec / (10 * NSEC_PER_MSEC); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* 1228c2ecf20Sopenharmony_ci * Record 00h value for OffsetFromUtc field and 1 value for OffsetValid 1238c2ecf20Sopenharmony_ci * to indicate that local time and UTC are the same. 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_ci *tz = EXFAT_TZ_VALID; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/* 1298c2ecf20Sopenharmony_ci * The timestamp for access_time has double seconds granularity. 1308c2ecf20Sopenharmony_ci * (There is no 10msIncrement field for access_time unlike create/modify_time) 1318c2ecf20Sopenharmony_ci * atime also has only a 2-second resolution. 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_civoid exfat_truncate_atime(struct timespec64 *ts) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci ts->tv_sec = round_down(ts->tv_sec, 2); 1368c2ecf20Sopenharmony_ci ts->tv_nsec = 0; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ciu16 exfat_calc_chksum16(void *data, int len, u16 chksum, int type) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci int i; 1428c2ecf20Sopenharmony_ci u8 *c = (u8 *)data; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci for (i = 0; i < len; i++, c++) { 1458c2ecf20Sopenharmony_ci if (unlikely(type == CS_DIR_ENTRY && (i == 2 || i == 3))) 1468c2ecf20Sopenharmony_ci continue; 1478c2ecf20Sopenharmony_ci chksum = ((chksum << 15) | (chksum >> 1)) + *c; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci return chksum; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ciu32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci int i; 1558c2ecf20Sopenharmony_ci u8 *c = (u8 *)data; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci for (i = 0; i < len; i++, c++) { 1588c2ecf20Sopenharmony_ci if (unlikely(type == CS_BOOT_SECTOR && 1598c2ecf20Sopenharmony_ci (i == 106 || i == 107 || i == 112))) 1608c2ecf20Sopenharmony_ci continue; 1618c2ecf20Sopenharmony_ci chksum = ((chksum << 31) | (chksum >> 1)) + *c; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci return chksum; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_civoid exfat_update_bh(struct buffer_head *bh, int sync) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci set_buffer_uptodate(bh); 1698c2ecf20Sopenharmony_ci mark_buffer_dirty(bh); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (sync) 1728c2ecf20Sopenharmony_ci sync_dirty_buffer(bh); 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ciint exfat_update_bhs(struct buffer_head **bhs, int nr_bhs, int sync) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci int i, err = 0; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci for (i = 0; i < nr_bhs; i++) { 1808c2ecf20Sopenharmony_ci set_buffer_uptodate(bhs[i]); 1818c2ecf20Sopenharmony_ci mark_buffer_dirty(bhs[i]); 1828c2ecf20Sopenharmony_ci if (sync) 1838c2ecf20Sopenharmony_ci write_dirty_buffer(bhs[i], 0); 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci for (i = 0; i < nr_bhs && sync; i++) { 1878c2ecf20Sopenharmony_ci wait_on_buffer(bhs[i]); 1888c2ecf20Sopenharmony_ci if (!err && !buffer_uptodate(bhs[i])) 1898c2ecf20Sopenharmony_ci err = -EIO; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci return err; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_civoid exfat_chain_set(struct exfat_chain *ec, unsigned int dir, 1958c2ecf20Sopenharmony_ci unsigned int size, unsigned char flags) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci ec->dir = dir; 1988c2ecf20Sopenharmony_ci ec->size = size; 1998c2ecf20Sopenharmony_ci ec->flags = flags; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_civoid exfat_chain_dup(struct exfat_chain *dup, struct exfat_chain *ec) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci return exfat_chain_set(dup, ec->dir, ec->size, ec->flags); 2058c2ecf20Sopenharmony_ci} 206