fs/ntfs3: Use new api for mounting

We have now new mount api as described in Documentation/filesystems. We
should use it as it gives us some benefits which are desribed here
lore.kernel.org/linux-fsdevel/159646178122.1784947.11705396571718464082.stgit@warthog.procyon.org.uk/

Nls loading is changed a to load with string. This did make code also
little cleaner.

Also try to use fsparam_flag_no as much as possible. This is just nice
little touch and is not mandatory but it should not make any harm. It
is just convenient that we can use example acl/noacl mount options.

Signed-off-by: Kari Argillander <kari.argillander@gmail.com>
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
This commit is contained in:
Kari Argillander 2021-09-07 18:35:52 +03:00 committed by Konstantin Komarov
parent 564c97bdfa
commit 610f8f5a7b
No known key found for this signature in database
GPG Key ID: A9B0331F832407B6
2 changed files with 229 additions and 205 deletions

View File

@ -52,6 +52,7 @@
// clang-format on
struct ntfs_mount_options {
char *nls_name;
struct nls_table *nls;
kuid_t fs_uid;

View File

@ -28,11 +28,12 @@
#include <linux/buffer_head.h>
#include <linux/exportfs.h>
#include <linux/fs.h>
#include <linux/fs_context.h>
#include <linux/fs_parser.h>
#include <linux/iversion.h>
#include <linux/log2.h>
#include <linux/module.h>
#include <linux/nls.h>
#include <linux/parser.h>
#include <linux/seq_file.h>
#include <linux/statfs.h>
@ -205,9 +206,11 @@ void *ntfs_put_shared(void *ptr)
return ret;
}
static inline void clear_mount_options(struct ntfs_mount_options *options)
static inline void put_mount_options(struct ntfs_mount_options *options)
{
kfree(options->nls_name);
unload_nls(options->nls);
kfree(options);
}
enum Opt {
@ -229,205 +232,171 @@ enum Opt {
Opt_err,
};
static const match_table_t ntfs_tokens = {
{ Opt_uid, "uid=%u" },
{ Opt_gid, "gid=%u" },
{ Opt_umask, "umask=%o" },
{ Opt_dmask, "dmask=%o" },
{ Opt_fmask, "fmask=%o" },
{ Opt_immutable, "sys_immutable" },
{ Opt_discard, "discard" },
{ Opt_force, "force" },
{ Opt_sparse, "sparse" },
{ Opt_nohidden, "nohidden" },
{ Opt_acl, "acl" },
{ Opt_showmeta, "showmeta" },
{ Opt_nls, "nls=%s" },
{ Opt_prealloc, "prealloc" },
{ Opt_no_acs_rules, "no_acs_rules" },
{ Opt_err, NULL },
static const struct fs_parameter_spec ntfs_fs_parameters[] = {
fsparam_u32("uid", Opt_uid),
fsparam_u32("gid", Opt_gid),
fsparam_u32oct("umask", Opt_umask),
fsparam_u32oct("dmask", Opt_dmask),
fsparam_u32oct("fmask", Opt_fmask),
fsparam_flag_no("sys_immutable", Opt_immutable),
fsparam_flag_no("discard", Opt_discard),
fsparam_flag_no("force", Opt_force),
fsparam_flag_no("sparse", Opt_sparse),
fsparam_flag("nohidden", Opt_nohidden),
fsparam_flag_no("acl", Opt_acl),
fsparam_flag_no("showmeta", Opt_showmeta),
fsparam_string("nls", Opt_nls),
fsparam_flag_no("prealloc", Opt_prealloc),
fsparam_flag("no_acs_rules", Opt_no_acs_rules),
{}
};
static noinline int ntfs_parse_options(struct super_block *sb, char *options,
int silent,
struct ntfs_mount_options *opts)
/*
* Load nls table or if @nls is utf8 then return NULL.
*/
static struct nls_table *ntfs_load_nls(char *nls)
{
char *p;
substring_t args[MAX_OPT_ARGS];
int option;
char nls_name[30];
struct nls_table *nls;
struct nls_table *ret;
opts->fs_uid = current_uid();
opts->fs_gid = current_gid();
opts->fs_fmask_inv = opts->fs_dmask_inv = ~current_umask();
nls_name[0] = 0;
if (!nls)
nls = CONFIG_NLS_DEFAULT;
if (!options)
goto out;
if (strcmp(nls, "utf8") == 0)
return NULL;
while ((p = strsep(&options, ","))) {
int token;
if (strcmp(nls, CONFIG_NLS_DEFAULT) == 0)
return load_nls_default();
if (!*p)
continue;
ret = load_nls(nls);
if (ret)
return ret;
token = match_token(p, ntfs_tokens, args);
switch (token) {
case Opt_immutable:
opts->sys_immutable = 1;
break;
case Opt_uid:
if (match_int(&args[0], &option))
return -EINVAL;
opts->fs_uid = make_kuid(current_user_ns(), option);
if (!uid_valid(opts->fs_uid))
return -EINVAL;
opts->uid = 1;
break;
case Opt_gid:
if (match_int(&args[0], &option))
return -EINVAL;
opts->fs_gid = make_kgid(current_user_ns(), option);
if (!gid_valid(opts->fs_gid))
return -EINVAL;
opts->gid = 1;
break;
case Opt_umask:
if (match_octal(&args[0], &option))
return -EINVAL;
opts->fs_fmask_inv = opts->fs_dmask_inv = ~option;
opts->fmask = opts->dmask = 1;
break;
case Opt_dmask:
if (match_octal(&args[0], &option))
return -EINVAL;
opts->fs_dmask_inv = ~option;
opts->dmask = 1;
break;
case Opt_fmask:
if (match_octal(&args[0], &option))
return -EINVAL;
opts->fs_fmask_inv = ~option;
opts->fmask = 1;
break;
case Opt_discard:
opts->discard = 1;
break;
case Opt_force:
opts->force = 1;
break;
case Opt_sparse:
opts->sparse = 1;
break;
case Opt_nohidden:
opts->nohidden = 1;
break;
case Opt_acl:
return ERR_PTR(-EINVAL);
}
static int ntfs_fs_parse_param(struct fs_context *fc,
struct fs_parameter *param)
{
struct ntfs_mount_options *opts = fc->fs_private;
struct fs_parse_result result;
int opt;
opt = fs_parse(fc, ntfs_fs_parameters, param, &result);
if (opt < 0)
return opt;
switch (opt) {
case Opt_uid:
opts->fs_uid = make_kuid(current_user_ns(), result.uint_32);
if (!uid_valid(opts->fs_uid))
return invalf(fc, "ntfs3: Invalid value for uid.");
opts->uid = 1;
break;
case Opt_gid:
opts->fs_gid = make_kgid(current_user_ns(), result.uint_32);
if (!gid_valid(opts->fs_gid))
return invalf(fc, "ntfs3: Invalid value for gid.");
opts->gid = 1;
break;
case Opt_umask:
if (result.uint_32 & ~07777)
return invalf(fc, "ntfs3: Invalid value for umask.");
opts->fs_fmask_inv = ~result.uint_32;
opts->fs_dmask_inv = ~result.uint_32;
opts->fmask = 1;
opts->dmask = 1;
break;
case Opt_dmask:
if (result.uint_32 & ~07777)
return invalf(fc, "ntfs3: Invalid value for dmask.");
opts->fs_dmask_inv = ~result.uint_32;
opts->dmask = 1;
break;
case Opt_fmask:
if (result.uint_32 & ~07777)
return invalf(fc, "ntfs3: Invalid value for fmask.");
opts->fs_fmask_inv = ~result.uint_32;
opts->fmask = 1;
break;
case Opt_immutable:
opts->sys_immutable = result.negated ? 0 : 1;
break;
case Opt_discard:
opts->discard = result.negated ? 0 : 1;
break;
case Opt_force:
opts->force = result.negated ? 0 : 1;
break;
case Opt_sparse:
opts->sparse = result.negated ? 0 : 1;
break;
case Opt_nohidden:
opts->nohidden = 1;
break;
case Opt_acl:
if (!result.negated)
#ifdef CONFIG_NTFS3_FS_POSIX_ACL
sb->s_flags |= SB_POSIXACL;
break;
fc->sb_flags |= SB_POSIXACL;
#else
ntfs_err(sb, "support for ACL not compiled in!");
return -EINVAL;
return invalf(fc, "ntfs3: Support for ACL not compiled in!");
#endif
case Opt_showmeta:
opts->showmeta = 1;
break;
case Opt_nls:
match_strlcpy(nls_name, &args[0], sizeof(nls_name));
break;
case Opt_prealloc:
opts->prealloc = 1;
break;
case Opt_no_acs_rules:
opts->no_acs_rules = 1;
break;
default:
if (!silent)
ntfs_err(
sb,
"Unrecognized mount option \"%s\" or missing value",
p);
//return -EINVAL;
}
else
fc->sb_flags &= ~SB_POSIXACL;
break;
case Opt_showmeta:
opts->showmeta = result.negated ? 0 : 1;
break;
case Opt_nls:
kfree(opts->nls_name);
opts->nls_name = param->string;
param->string = NULL;
break;
case Opt_prealloc:
opts->prealloc = result.negated ? 0 : 1;
break;
case Opt_no_acs_rules:
opts->no_acs_rules = 1;
break;
default:
/* Should not be here unless we forget add case. */
return -EINVAL;
}
out:
if (!strcmp(nls_name[0] ? nls_name : CONFIG_NLS_DEFAULT, "utf8")) {
/*
* For UTF-8 use utf16s_to_utf8s()/utf8s_to_utf16s()
* instead of NLS.
*/
nls = NULL;
} else if (nls_name[0]) {
nls = load_nls(nls_name);
if (!nls) {
ntfs_err(sb, "failed to load \"%s\"", nls_name);
return -EINVAL;
}
} else {
nls = load_nls_default();
if (!nls) {
ntfs_err(sb, "failed to load default nls");
return -EINVAL;
}
}
opts->nls = nls;
return 0;
}
static int ntfs_remount(struct super_block *sb, int *flags, char *data)
static int ntfs_fs_reconfigure(struct fs_context *fc)
{
int err, ro_rw;
struct super_block *sb = fc->root->d_sb;
struct ntfs_sb_info *sbi = sb->s_fs_info;
struct ntfs_mount_options old_opts;
char *orig_data = kstrdup(data, GFP_KERNEL);
struct ntfs_mount_options *new_opts = fc->fs_private;
int ro_rw;
if (data && !orig_data)
return -ENOMEM;
/* Store original options. */
memcpy(&old_opts, sbi->options, sizeof(old_opts));
clear_mount_options(sbi->options);
memset(sbi->options, 0, sizeof(old_opts));
err = ntfs_parse_options(sb, data, 0, sbi->options);
if (err)
goto restore_opts;
ro_rw = sb_rdonly(sb) && !(*flags & SB_RDONLY);
ro_rw = sb_rdonly(sb) && !(fc->sb_flags & SB_RDONLY);
if (ro_rw && (sbi->flags & NTFS_FLAGS_NEED_REPLAY)) {
ntfs_warn(
sb,
"Couldn't remount rw because journal is not replayed. Please umount/remount instead\n");
err = -EINVAL;
goto restore_opts;
errorf(fc, "ntfs3: Couldn't remount rw because journal is not replayed. Please umount/remount instead\n");
return -EINVAL;
}
new_opts->nls = ntfs_load_nls(new_opts->nls_name);
if (IS_ERR(new_opts->nls)) {
new_opts->nls = NULL;
errorf(fc, "ntfs3: Cannot load nls %s", new_opts->nls_name);
return -EINVAL;
}
if (new_opts->nls != sbi->options->nls)
return invalf(fc, "ntfs3: Cannot use different nls when remounting!");
sync_filesystem(sb);
if (ro_rw && (sbi->volume.flags & VOLUME_FLAG_DIRTY) &&
!sbi->options->force) {
ntfs_warn(sb, "volume is dirty and \"force\" flag is not set!");
err = -EINVAL;
goto restore_opts;
!new_opts->force) {
errorf(fc, "ntfs3: Volume is dirty and \"force\" flag is not set!");
return -EINVAL;
}
clear_mount_options(&old_opts);
memcpy(sbi->options, new_opts, sizeof(*new_opts));
ntfs_info(sb, "re-mounted. Opts: %s", orig_data);
err = 0;
goto out;
restore_opts:
clear_mount_options(sbi->options);
memcpy(sbi->options, &old_opts, sizeof(old_opts));
out:
kfree(orig_data);
return err;
return 0;
}
static struct kmem_cache *ntfs_inode_cachep;
@ -506,9 +475,6 @@ static noinline void put_ntfs(struct ntfs_sb_info *sbi)
xpress_free_decompressor(sbi->compress.xpress);
lzx_free_decompressor(sbi->compress.lzx);
#endif
clear_mount_options(sbi->options);
kfree(sbi->options);
kfree(sbi);
}
@ -519,7 +485,9 @@ static void ntfs_put_super(struct super_block *sb)
/* Mark rw ntfs as clear, if possible. */
ntfs_set_state(sbi, NTFS_DIRTY_CLEAR);
put_mount_options(sbi->options);
put_ntfs(sbi);
sb->s_fs_info = NULL;
sync_blockdev(sb->s_bdev);
}
@ -635,7 +603,6 @@ static const struct super_operations ntfs_sops = {
.statfs = ntfs_statfs,
.show_options = ntfs_show_options,
.sync_fs = ntfs_sync_fs,
.remount_fs = ntfs_remount,
.write_inode = ntfs3_write_inode,
};
@ -905,10 +872,10 @@ out:
/*
* ntfs_fill_super - Try to mount.
*/
static int ntfs_fill_super(struct super_block *sb, void *data, int silent)
static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
{
int err;
struct ntfs_sb_info *sbi;
struct ntfs_sb_info *sbi = sb->s_fs_info;
struct block_device *bdev = sb->s_bdev;
struct inode *bd_inode = bdev->bd_inode;
struct request_queue *rq = bdev_get_queue(bdev);
@ -927,17 +894,6 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent)
ref.high = 0;
sbi = kzalloc(sizeof(struct ntfs_sb_info), GFP_NOFS);
if (!sbi)
return -ENOMEM;
sbi->options = kzalloc(sizeof(struct ntfs_mount_options), GFP_NOFS);
if (!sbi->options) {
kfree(sbi);
return -ENOMEM;
}
sb->s_fs_info = sbi;
sbi->sb = sb;
sb->s_flags |= SB_NODIRATIME;
sb->s_magic = 0x7366746e; // "ntfs"
@ -949,9 +905,12 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent)
ratelimit_state_init(&sbi->msg_ratelimit, DEFAULT_RATELIMIT_INTERVAL,
DEFAULT_RATELIMIT_BURST);
err = ntfs_parse_options(sb, data, silent, sbi->options);
if (err)
goto out;
sbi->options->nls = ntfs_load_nls(sbi->options->nls_name);
if (IS_ERR(sbi->options->nls)) {
sbi->options->nls = NULL;
errorf(fc, "Cannot load nls %s", sbi->options->nls_name);
return -EINVAL;
}
if (!rq || !blk_queue_discard(rq) || !rq->limits.discard_granularity) {
;
@ -1344,6 +1303,9 @@ load_root:
goto out;
}
fc->fs_private = NULL;
fc->s_fs_info = NULL;
return 0;
out:
@ -1354,9 +1316,6 @@ out:
sb->s_root = NULL;
}
put_ntfs(sbi);
sb->s_fs_info = NULL;
return err;
}
@ -1426,19 +1385,83 @@ int ntfs_discard(struct ntfs_sb_info *sbi, CLST lcn, CLST len)
return err;
}
static struct dentry *ntfs_mount(struct file_system_type *fs_type, int flags,
const char *dev_name, void *data)
static int ntfs_fs_get_tree(struct fs_context *fc)
{
return mount_bdev(fs_type, flags, dev_name, data, ntfs_fill_super);
return get_tree_bdev(fc, ntfs_fill_super);
}
/*
* ntfs_fs_free - Free fs_context.
*
* Note that this will be called after fill_super and reconfigure
* even when they pass. So they have to take pointers if they pass.
*/
static void ntfs_fs_free(struct fs_context *fc)
{
struct ntfs_mount_options *opts = fc->fs_private;
struct ntfs_sb_info *sbi = fc->s_fs_info;
if (sbi)
put_ntfs(sbi);
if (opts)
put_mount_options(opts);
}
static const struct fs_context_operations ntfs_context_ops = {
.parse_param = ntfs_fs_parse_param,
.get_tree = ntfs_fs_get_tree,
.reconfigure = ntfs_fs_reconfigure,
.free = ntfs_fs_free,
};
/*
* ntfs_init_fs_context - Initialize spi and opts
*
* This will called when mount/remount. We will first initiliaze
* options so that if remount we can use just that.
*/
static int ntfs_init_fs_context(struct fs_context *fc)
{
struct ntfs_mount_options *opts;
struct ntfs_sb_info *sbi;
opts = kzalloc(sizeof(struct ntfs_mount_options), GFP_NOFS);
if (!opts)
return -ENOMEM;
/* Default options. */
opts->fs_uid = current_uid();
opts->fs_gid = current_gid();
opts->fs_fmask_inv = ~current_umask();
opts->fs_dmask_inv = ~current_umask();
if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE)
goto ok;
sbi = kzalloc(sizeof(struct ntfs_sb_info), GFP_NOFS);
if (!sbi) {
kfree(opts);
return -ENOMEM;
}
sbi->options = opts;
fc->s_fs_info = sbi;
ok:
fc->fs_private = opts;
fc->ops = &ntfs_context_ops;
return 0;
}
// clang-format off
static struct file_system_type ntfs_fs_type = {
.owner = THIS_MODULE,
.name = "ntfs3",
.mount = ntfs_mount,
.kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP,
.owner = THIS_MODULE,
.name = "ntfs3",
.init_fs_context = ntfs_init_fs_context,
.parameters = ntfs_fs_parameters,
.kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP,
};
// clang-format on