162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Written 1992,1993 by Werner Almesberger 462306a36Sopenharmony_ci * 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980 562306a36Sopenharmony_ci * and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru) 662306a36Sopenharmony_ci * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/time.h> 1062306a36Sopenharmony_ci#include <linux/fs.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/buffer_head.h> 1362306a36Sopenharmony_ci#include <linux/blk_types.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "exfat_raw.h" 1662306a36Sopenharmony_ci#include "exfat_fs.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* 1962306a36Sopenharmony_ci * exfat_fs_error reports a file system problem that might indicate fa data 2062306a36Sopenharmony_ci * corruption/inconsistency. Depending on 'errors' mount option the 2162306a36Sopenharmony_ci * panic() is called, or error message is printed FAT and nothing is done, 2262306a36Sopenharmony_ci * or filesystem is remounted read-only (default behavior). 2362306a36Sopenharmony_ci * In case the file system is remounted read-only, it can be made writable 2462306a36Sopenharmony_ci * again by remounting it. 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_civoid __exfat_fs_error(struct super_block *sb, int report, const char *fmt, ...) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci struct exfat_mount_options *opts = &EXFAT_SB(sb)->options; 2962306a36Sopenharmony_ci va_list args; 3062306a36Sopenharmony_ci struct va_format vaf; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci if (report) { 3362306a36Sopenharmony_ci va_start(args, fmt); 3462306a36Sopenharmony_ci vaf.fmt = fmt; 3562306a36Sopenharmony_ci vaf.va = &args; 3662306a36Sopenharmony_ci exfat_err(sb, "error, %pV", &vaf); 3762306a36Sopenharmony_ci va_end(args); 3862306a36Sopenharmony_ci } 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (opts->errors == EXFAT_ERRORS_PANIC) { 4162306a36Sopenharmony_ci panic("exFAT-fs (%s): fs panic from previous error\n", 4262306a36Sopenharmony_ci sb->s_id); 4362306a36Sopenharmony_ci } else if (opts->errors == EXFAT_ERRORS_RO && !sb_rdonly(sb)) { 4462306a36Sopenharmony_ci sb->s_flags |= SB_RDONLY; 4562306a36Sopenharmony_ci exfat_err(sb, "Filesystem has been set read-only"); 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define SECS_PER_MIN (60) 5062306a36Sopenharmony_ci#define TIMEZONE_SEC(x) ((x) * 15 * SECS_PER_MIN) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic void exfat_adjust_tz(struct timespec64 *ts, u8 tz_off) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci if (tz_off <= 0x3F) 5562306a36Sopenharmony_ci ts->tv_sec -= TIMEZONE_SEC(tz_off); 5662306a36Sopenharmony_ci else /* 0x40 <= (tz_off & 0x7F) <=0x7F */ 5762306a36Sopenharmony_ci ts->tv_sec += TIMEZONE_SEC(0x80 - tz_off); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic inline int exfat_tz_offset(struct exfat_sb_info *sbi) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci if (sbi->options.sys_tz) 6362306a36Sopenharmony_ci return -sys_tz.tz_minuteswest; 6462306a36Sopenharmony_ci return sbi->options.time_offset; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* Convert a EXFAT time/date pair to a UNIX date (seconds since 1 1 70). */ 6862306a36Sopenharmony_civoid exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts, 6962306a36Sopenharmony_ci u8 tz, __le16 time, __le16 date, u8 time_cs) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci u16 t = le16_to_cpu(time); 7262306a36Sopenharmony_ci u16 d = le16_to_cpu(date); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci ts->tv_sec = mktime64(1980 + (d >> 9), d >> 5 & 0x000F, d & 0x001F, 7562306a36Sopenharmony_ci t >> 11, (t >> 5) & 0x003F, (t & 0x001F) << 1); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci /* time_cs field represent 0 ~ 199cs(1990 ms) */ 7962306a36Sopenharmony_ci if (time_cs) { 8062306a36Sopenharmony_ci ts->tv_sec += time_cs / 100; 8162306a36Sopenharmony_ci ts->tv_nsec = (time_cs % 100) * 10 * NSEC_PER_MSEC; 8262306a36Sopenharmony_ci } else 8362306a36Sopenharmony_ci ts->tv_nsec = 0; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (tz & EXFAT_TZ_VALID) 8662306a36Sopenharmony_ci /* Adjust timezone to UTC0. */ 8762306a36Sopenharmony_ci exfat_adjust_tz(ts, tz & ~EXFAT_TZ_VALID); 8862306a36Sopenharmony_ci else 8962306a36Sopenharmony_ci ts->tv_sec -= exfat_tz_offset(sbi) * SECS_PER_MIN; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* Convert linear UNIX date to a EXFAT time/date pair. */ 9362306a36Sopenharmony_civoid exfat_set_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts, 9462306a36Sopenharmony_ci u8 *tz, __le16 *time, __le16 *date, u8 *time_cs) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci struct tm tm; 9762306a36Sopenharmony_ci u16 t, d; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci time64_to_tm(ts->tv_sec, 0, &tm); 10062306a36Sopenharmony_ci t = (tm.tm_hour << 11) | (tm.tm_min << 5) | (tm.tm_sec >> 1); 10162306a36Sopenharmony_ci d = ((tm.tm_year - 80) << 9) | ((tm.tm_mon + 1) << 5) | tm.tm_mday; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci *time = cpu_to_le16(t); 10462306a36Sopenharmony_ci *date = cpu_to_le16(d); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* time_cs field represent 0 ~ 199cs(1990 ms) */ 10762306a36Sopenharmony_ci if (time_cs) 10862306a36Sopenharmony_ci *time_cs = (tm.tm_sec & 1) * 100 + 10962306a36Sopenharmony_ci ts->tv_nsec / (10 * NSEC_PER_MSEC); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* 11262306a36Sopenharmony_ci * Record 00h value for OffsetFromUtc field and 1 value for OffsetValid 11362306a36Sopenharmony_ci * to indicate that local time and UTC are the same. 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_ci *tz = EXFAT_TZ_VALID; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* 11962306a36Sopenharmony_ci * The timestamp for access_time has double seconds granularity. 12062306a36Sopenharmony_ci * (There is no 10msIncrement field for access_time unlike create/modify_time) 12162306a36Sopenharmony_ci * atime also has only a 2-second resolution. 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_civoid exfat_truncate_atime(struct timespec64 *ts) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci ts->tv_sec = round_down(ts->tv_sec, 2); 12662306a36Sopenharmony_ci ts->tv_nsec = 0; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ciu16 exfat_calc_chksum16(void *data, int len, u16 chksum, int type) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci int i; 13262306a36Sopenharmony_ci u8 *c = (u8 *)data; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci for (i = 0; i < len; i++, c++) { 13562306a36Sopenharmony_ci if (unlikely(type == CS_DIR_ENTRY && (i == 2 || i == 3))) 13662306a36Sopenharmony_ci continue; 13762306a36Sopenharmony_ci chksum = ((chksum << 15) | (chksum >> 1)) + *c; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci return chksum; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ciu32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci int i; 14562306a36Sopenharmony_ci u8 *c = (u8 *)data; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci for (i = 0; i < len; i++, c++) { 14862306a36Sopenharmony_ci if (unlikely(type == CS_BOOT_SECTOR && 14962306a36Sopenharmony_ci (i == 106 || i == 107 || i == 112))) 15062306a36Sopenharmony_ci continue; 15162306a36Sopenharmony_ci chksum = ((chksum << 31) | (chksum >> 1)) + *c; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci return chksum; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_civoid exfat_update_bh(struct buffer_head *bh, int sync) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci set_buffer_uptodate(bh); 15962306a36Sopenharmony_ci mark_buffer_dirty(bh); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (sync) 16262306a36Sopenharmony_ci sync_dirty_buffer(bh); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ciint exfat_update_bhs(struct buffer_head **bhs, int nr_bhs, int sync) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci int i, err = 0; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci for (i = 0; i < nr_bhs; i++) { 17062306a36Sopenharmony_ci set_buffer_uptodate(bhs[i]); 17162306a36Sopenharmony_ci mark_buffer_dirty(bhs[i]); 17262306a36Sopenharmony_ci if (sync) 17362306a36Sopenharmony_ci write_dirty_buffer(bhs[i], REQ_SYNC); 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci for (i = 0; i < nr_bhs && sync; i++) { 17762306a36Sopenharmony_ci wait_on_buffer(bhs[i]); 17862306a36Sopenharmony_ci if (!err && !buffer_uptodate(bhs[i])) 17962306a36Sopenharmony_ci err = -EIO; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci return err; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_civoid exfat_chain_set(struct exfat_chain *ec, unsigned int dir, 18562306a36Sopenharmony_ci unsigned int size, unsigned char flags) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci ec->dir = dir; 18862306a36Sopenharmony_ci ec->size = size; 18962306a36Sopenharmony_ci ec->flags = flags; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_civoid exfat_chain_dup(struct exfat_chain *dup, struct exfat_chain *ec) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci return exfat_chain_set(dup, ec->dir, ec->size, ec->flags); 19562306a36Sopenharmony_ci} 196