exfat_init_fs_context() only inherits uid, gid, fmask and dmask for a reconfigure. But exfat_reconfigure() refuses to change iocharset, keep_last_dots, sys_tz, time_offset and allow_utime. So every remount that doesn't repeat a non-default value for one of them fails with EINVAL, including emergency remount (sysrq u) which passes no options at all. And an option-less remount silently resets errors=, zero_size_dir and discard. So start a reconfigure from the current options like gfs2 does. The iocharset string stays NULL in the new context because ->init_fs_context() runs without s_umount for mount(2) and a concurrent remount could free it. It's only compared if the remount names one. A shutdown turns discard off for good so exfat_reconfigure() keeps it off. The next patch stops exfat from setting SB_RDONLY after errors. An errored exfat then stays read-write and emergency remount would run into this as well. Fixes: e6fd5d3a4317 ("exfat: support modifying mount options via remount") Cc: stable@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/exfat/super.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/fs/exfat/super.c b/fs/exfat/super.c index 4943cef97741..44d2000007df 100644 --- a/fs/exfat/super.c +++ b/fs/exfat/super.c @@ -797,14 +797,12 @@ static int exfat_reconfigure(struct fs_context *fc) mutex_unlock(&sbi->s_lock); } - if (new_opts->allow_utime == (unsigned short)-1) - new_opts->allow_utime = ~new_opts->fs_dmask & 0022; - /* * Since the old settings of these mount options are cached in * inodes or dentries, they cannot be modified dynamically. */ - if (strcmp(new_opts->iocharset, cur_opts->iocharset) || + if ((new_opts->iocharset && + strcmp(new_opts->iocharset, cur_opts->iocharset)) || new_opts->keep_last_dots != cur_opts->keep_last_dots || new_opts->sys_tz != cur_opts->sys_tz || new_opts->time_offset != cur_opts->time_offset || @@ -815,6 +813,10 @@ static int exfat_reconfigure(struct fs_context *fc) new_opts->allow_utime != cur_opts->allow_utime) return -EINVAL; + /* A shutdown turned discard off for good. */ + if (exfat_forced_shutdown(sb)) + new_opts->discard = 0; + if (new_opts->discard != cur_opts->discard && new_opts->discard && !bdev_max_discard_sectors(sb->s_bdev)) { @@ -823,6 +825,8 @@ static int exfat_reconfigure(struct fs_context *fc) } swap(*cur_opts, *new_opts); + /* The iocharset can't change, keep the current string. */ + swap(cur_opts->iocharset, new_opts->iocharset); return 0; } @@ -849,23 +853,24 @@ static int exfat_init_fs_context(struct fs_context *fc) if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE && fc->root) { struct super_block *sb = fc->root->d_sb; - struct exfat_mount_options *cur_opts = &EXFAT_SB(sb)->options; - sbi->options.fs_uid = cur_opts->fs_uid; - sbi->options.fs_gid = cur_opts->fs_gid; - sbi->options.fs_fmask = cur_opts->fs_fmask; - sbi->options.fs_dmask = cur_opts->fs_dmask; + /* + * Start from the current options so a remount only changes + * what it specifies. The iocharset string stays with the + * superblock, see exfat_reconfigure(). + */ + sbi->options = EXFAT_SB(sb)->options; + sbi->options.iocharset = NULL; } else { sbi->options.fs_uid = current_uid(); sbi->options.fs_gid = current_gid(); sbi->options.fs_fmask = current->fs->umask; sbi->options.fs_dmask = current->fs->umask; + sbi->options.allow_utime = -1; + sbi->options.errors = EXFAT_ERRORS_RO; + exfat_set_iocharset(&sbi->options, exfat_default_iocharset); } - sbi->options.allow_utime = -1; - sbi->options.errors = EXFAT_ERRORS_RO; - exfat_set_iocharset(&sbi->options, exfat_default_iocharset); - fc->s_fs_info = sbi; fc->ops = &exfat_context_ops; return 0; -- 2.53.0