exfat: introduce mount option 'sys_tz'

EXFAT_TZ_VALID bit in {create,modify,access}_tz is corresponding to
OffsetValid field in exfat specification [1]. When this bit isn't
set, timestamps should be treated as having the same UTC offset as
the current local time.

Currently, there is an option 'time_offset' for users to specify the
UTC offset for this issue. This patch introduces a new mount option
'sys_tz' to use system timezone as time offset.

Link: [1] https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification#74102-offsetvalid-field

Signed-off-by: Chung-Chiang Cheng <cccheng@synology.com>
Acked-by: Sungjong Seo <sj1557.seo@samsung.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
Chung-Chiang Cheng 2022-04-06 17:55:52 +08:00 committed by Namjae Jeon
parent d8dad2588a
commit 9b002894b4
3 changed files with 17 additions and 3 deletions

View file

@ -203,6 +203,7 @@ struct exfat_mount_options {
/* on error: continue, panic, remount-ro */ /* on error: continue, panic, remount-ro */
enum exfat_error_mode errors; enum exfat_error_mode errors;
unsigned utf8:1, /* Use of UTF-8 character set */ unsigned utf8:1, /* Use of UTF-8 character set */
sys_tz:1, /* Use local timezone */
discard:1, /* Issue discard requests on deletions */ discard:1, /* Issue discard requests on deletions */
keep_last_dots:1; /* Keep trailing periods in paths */ keep_last_dots:1; /* Keep trailing periods in paths */
int time_offset; /* Offset of timestamps from UTC (in minutes) */ int time_offset; /* Offset of timestamps from UTC (in minutes) */

View file

@ -74,6 +74,13 @@ static void exfat_adjust_tz(struct timespec64 *ts, u8 tz_off)
ts->tv_sec += TIMEZONE_SEC(0x80 - tz_off); ts->tv_sec += TIMEZONE_SEC(0x80 - tz_off);
} }
static inline int exfat_tz_offset(struct exfat_sb_info *sbi)
{
if (sbi->options.sys_tz)
return -sys_tz.tz_minuteswest;
return sbi->options.time_offset;
}
/* Convert a EXFAT time/date pair to a UNIX date (seconds since 1 1 70). */ /* Convert a EXFAT time/date pair to a UNIX date (seconds since 1 1 70). */
void exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts, void exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
u8 tz, __le16 time, __le16 date, u8 time_cs) u8 tz, __le16 time, __le16 date, u8 time_cs)
@ -96,8 +103,7 @@ void exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
/* Adjust timezone to UTC0. */ /* Adjust timezone to UTC0. */
exfat_adjust_tz(ts, tz & ~EXFAT_TZ_VALID); exfat_adjust_tz(ts, tz & ~EXFAT_TZ_VALID);
else else
/* Convert from local time to UTC using time_offset. */ ts->tv_sec -= exfat_tz_offset(sbi) * SECS_PER_MIN;
ts->tv_sec -= sbi->options.time_offset * SECS_PER_MIN;
} }
/* Convert linear UNIX date to a EXFAT time/date pair. */ /* Convert linear UNIX date to a EXFAT time/date pair. */

View file

@ -170,7 +170,9 @@ static int exfat_show_options(struct seq_file *m, struct dentry *root)
seq_puts(m, ",discard"); seq_puts(m, ",discard");
if (opts->keep_last_dots) if (opts->keep_last_dots)
seq_puts(m, ",keep_last_dots"); seq_puts(m, ",keep_last_dots");
if (opts->time_offset) if (opts->sys_tz)
seq_puts(m, ",sys_tz");
else if (opts->time_offset)
seq_printf(m, ",time_offset=%d", opts->time_offset); seq_printf(m, ",time_offset=%d", opts->time_offset);
return 0; return 0;
} }
@ -214,6 +216,7 @@ enum {
Opt_errors, Opt_errors,
Opt_discard, Opt_discard,
Opt_keep_last_dots, Opt_keep_last_dots,
Opt_sys_tz,
Opt_time_offset, Opt_time_offset,
/* Deprecated options */ /* Deprecated options */
@ -241,6 +244,7 @@ static const struct fs_parameter_spec exfat_parameters[] = {
fsparam_enum("errors", Opt_errors, exfat_param_enums), fsparam_enum("errors", Opt_errors, exfat_param_enums),
fsparam_flag("discard", Opt_discard), fsparam_flag("discard", Opt_discard),
fsparam_flag("keep_last_dots", Opt_keep_last_dots), fsparam_flag("keep_last_dots", Opt_keep_last_dots),
fsparam_flag("sys_tz", Opt_sys_tz),
fsparam_s32("time_offset", Opt_time_offset), fsparam_s32("time_offset", Opt_time_offset),
__fsparam(NULL, "utf8", Opt_utf8, fs_param_deprecated, __fsparam(NULL, "utf8", Opt_utf8, fs_param_deprecated,
NULL), NULL),
@ -298,6 +302,9 @@ static int exfat_parse_param(struct fs_context *fc, struct fs_parameter *param)
case Opt_keep_last_dots: case Opt_keep_last_dots:
opts->keep_last_dots = 1; opts->keep_last_dots = 1;
break; break;
case Opt_sys_tz:
opts->sys_tz = 1;
break;
case Opt_time_offset: case Opt_time_offset:
/* /*
* Make the limit 24 just in case someone invents something * Make the limit 24 just in case someone invents something