jfs: convert jfs to use the new mount api

Convert the jfs filesystem to use the new mount API.
Tested by comparing random mount & remount options before and after
the change.

Signed-off-by: Eric Sandeen <sandeen@redhat.com>
Link: https://lore.kernel.org/r/20240926171947.682881-1-sandeen@redhat.com
Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
Eric Sandeen 2024-09-26 12:19:46 -05:00 committed by Christian Brauner
parent 5b00a0f96d
commit 945be8ca81
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2
2 changed files with 245 additions and 221 deletions

View File

@ -24,6 +24,7 @@
#define JFS_ERR_REMOUNT_RO 0x00000002 /* remount read-only */ #define JFS_ERR_REMOUNT_RO 0x00000002 /* remount read-only */
#define JFS_ERR_CONTINUE 0x00000004 /* continue */ #define JFS_ERR_CONTINUE 0x00000004 /* continue */
#define JFS_ERR_PANIC 0x00000008 /* panic */ #define JFS_ERR_PANIC 0x00000008 /* panic */
#define JFS_ERR_MASK (JFS_ERR_REMOUNT_RO|JFS_ERR_CONTINUE|JFS_ERR_PANIC)
/* Quota support */ /* Quota support */
#define JFS_USRQUOTA 0x00000010 #define JFS_USRQUOTA 0x00000010

View File

@ -6,11 +6,11 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/parser.h>
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/vfs.h> #include <linux/vfs.h>
#include <linux/quotaops.h> #include <linux/quotaops.h>
#include <linux/mount.h> #include <linux/fs_context.h>
#include <linux/fs_parser.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/posix_acl.h> #include <linux/posix_acl.h>
@ -210,240 +210,195 @@ enum {
Opt_discard, Opt_nodiscard, Opt_discard_minblk Opt_discard, Opt_nodiscard, Opt_discard_minblk
}; };
static const match_table_t tokens = { static const struct constant_table jfs_param_errors[] = {
{Opt_integrity, "integrity"}, {"continue", JFS_ERR_CONTINUE},
{Opt_nointegrity, "nointegrity"}, {"remount-ro", JFS_ERR_REMOUNT_RO},
{Opt_iocharset, "iocharset=%s"}, {"panic", JFS_ERR_PANIC},
{Opt_resize, "resize=%u"}, {}
{Opt_resize_nosize, "resize"},
{Opt_errors, "errors=%s"},
{Opt_ignore, "noquota"},
{Opt_quota, "quota"},
{Opt_usrquota, "usrquota"},
{Opt_grpquota, "grpquota"},
{Opt_uid, "uid=%u"},
{Opt_gid, "gid=%u"},
{Opt_umask, "umask=%u"},
{Opt_discard, "discard"},
{Opt_nodiscard, "nodiscard"},
{Opt_discard_minblk, "discard=%u"},
{Opt_err, NULL}
}; };
static int parse_options(char *options, struct super_block *sb, s64 *newLVSize, static const struct fs_parameter_spec jfs_param_spec[] = {
int *flag) fsparam_flag_no ("integrity", Opt_integrity),
fsparam_string ("iocharset", Opt_iocharset),
fsparam_u64 ("resize", Opt_resize),
fsparam_flag ("resize", Opt_resize_nosize),
fsparam_enum ("errors", Opt_errors, jfs_param_errors),
fsparam_flag ("quota", Opt_quota),
fsparam_flag ("noquota", Opt_ignore),
fsparam_flag ("usrquota", Opt_usrquota),
fsparam_flag ("grpquota", Opt_grpquota),
fsparam_uid ("uid", Opt_uid),
fsparam_gid ("gid", Opt_gid),
fsparam_u32oct ("umask", Opt_umask),
fsparam_flag ("discard", Opt_discard),
fsparam_u32 ("discard", Opt_discard_minblk),
fsparam_flag ("nodiscard", Opt_nodiscard),
{}
};
struct jfs_context {
int flag;
kuid_t uid;
kgid_t gid;
uint umask;
uint minblks_trim;
void *nls_map;
bool resize;
s64 newLVSize;
};
static int jfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
{ {
void *nls_map = (void *)-1; /* -1: no change; NULL: none */ struct jfs_context *ctx = fc->fs_private;
char *p; int reconfigure = (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE);
struct jfs_sb_info *sbi = JFS_SBI(sb); struct fs_parse_result result;
struct nls_table *nls_map;
int opt;
*newLVSize = 0; opt = fs_parse(fc, jfs_param_spec, param, &result);
if (opt < 0)
return opt;
if (!options) switch (opt) {
return 1; case Opt_integrity:
if (result.negated)
while ((p = strsep(&options, ",")) != NULL) { ctx->flag |= JFS_NOINTEGRITY;
substring_t args[MAX_OPT_ARGS]; else
int token; ctx->flag &= ~JFS_NOINTEGRITY;
if (!*p) break;
continue; case Opt_ignore:
/* Silently ignore the quota options */
token = match_token(p, tokens, args); /* Don't do anything ;-) */
switch (token) { break;
case Opt_integrity: case Opt_iocharset:
*flag &= ~JFS_NOINTEGRITY; if (ctx->nls_map && ctx->nls_map != (void *) -1) {
break; unload_nls(ctx->nls_map);
case Opt_nointegrity: ctx->nls_map = NULL;
*flag |= JFS_NOINTEGRITY; }
break; if (!strcmp(param->string, "none"))
case Opt_ignore: ctx->nls_map = NULL;
/* Silently ignore the quota options */ else {
/* Don't do anything ;-) */ nls_map = load_nls(param->string);
break; if (!nls_map) {
case Opt_iocharset: pr_err("JFS: charset not found\n");
if (nls_map && nls_map != (void *) -1) return -EINVAL;
unload_nls(nls_map);
if (!strcmp(args[0].from, "none"))
nls_map = NULL;
else {
nls_map = load_nls(args[0].from);
if (!nls_map) {
pr_err("JFS: charset not found\n");
goto cleanup;
}
} }
break; ctx->nls_map = nls_map;
case Opt_resize:
{
char *resize = args[0].from;
int rc = kstrtoll(resize, 0, newLVSize);
if (rc)
goto cleanup;
break;
}
case Opt_resize_nosize:
{
*newLVSize = sb_bdev_nr_blocks(sb);
if (*newLVSize == 0)
pr_err("JFS: Cannot determine volume size\n");
break;
}
case Opt_errors:
{
char *errors = args[0].from;
if (!errors || !*errors)
goto cleanup;
if (!strcmp(errors, "continue")) {
*flag &= ~JFS_ERR_REMOUNT_RO;
*flag &= ~JFS_ERR_PANIC;
*flag |= JFS_ERR_CONTINUE;
} else if (!strcmp(errors, "remount-ro")) {
*flag &= ~JFS_ERR_CONTINUE;
*flag &= ~JFS_ERR_PANIC;
*flag |= JFS_ERR_REMOUNT_RO;
} else if (!strcmp(errors, "panic")) {
*flag &= ~JFS_ERR_CONTINUE;
*flag &= ~JFS_ERR_REMOUNT_RO;
*flag |= JFS_ERR_PANIC;
} else {
pr_err("JFS: %s is an invalid error handler\n",
errors);
goto cleanup;
}
break;
} }
break;
case Opt_resize:
if (!reconfigure)
return -EINVAL;
ctx->resize = true;
ctx->newLVSize = result.uint_64;
break;
case Opt_resize_nosize:
if (!reconfigure)
return -EINVAL;
ctx->resize = true;
break;
case Opt_errors:
ctx->flag &= ~JFS_ERR_MASK;
ctx->flag |= result.uint_32;
break;
#ifdef CONFIG_QUOTA #ifdef CONFIG_QUOTA
case Opt_quota: case Opt_quota:
case Opt_usrquota: case Opt_usrquota:
*flag |= JFS_USRQUOTA; ctx->flag |= JFS_USRQUOTA;
break; break;
case Opt_grpquota: case Opt_grpquota:
*flag |= JFS_GRPQUOTA; ctx->flag |= JFS_GRPQUOTA;
break; break;
#else #else
case Opt_usrquota: case Opt_usrquota:
case Opt_grpquota: case Opt_grpquota:
case Opt_quota: case Opt_quota:
pr_err("JFS: quota operations not supported\n"); pr_err("JFS: quota operations not supported\n");
break; break;
#endif #endif
case Opt_uid: case Opt_uid:
{ ctx->uid = result.uid;
char *uid = args[0].from; break;
uid_t val;
int rc = kstrtouint(uid, 0, &val);
if (rc) case Opt_gid:
goto cleanup; ctx->gid = result.gid;
sbi->uid = make_kuid(current_user_ns(), val); break;
if (!uid_valid(sbi->uid))
goto cleanup; case Opt_umask:
break; if (result.uint_32 & ~0777) {
pr_err("JFS: Invalid value of umask\n");
return -EINVAL;
} }
ctx->umask = result.uint_32;
break;
case Opt_gid: case Opt_discard:
{ /* if set to 1, even copying files will cause
char *gid = args[0].from; * trimming :O
gid_t val; * -> user has more control over the online trimming
int rc = kstrtouint(gid, 0, &val); */
ctx->minblks_trim = 64;
ctx->flag |= JFS_DISCARD;
break;
if (rc) case Opt_nodiscard:
goto cleanup; ctx->flag &= ~JFS_DISCARD;
sbi->gid = make_kgid(current_user_ns(), val); break;
if (!gid_valid(sbi->gid))
goto cleanup;
break;
}
case Opt_umask: case Opt_discard_minblk:
{ ctx->minblks_trim = result.uint_32;
char *umask = args[0].from; ctx->flag |= JFS_DISCARD;
int rc = kstrtouint(umask, 8, &sbi->umask); break;
if (rc) default:
goto cleanup; return -EINVAL;
if (sbi->umask & ~0777) {
pr_err("JFS: Invalid value of umask\n");
goto cleanup;
}
break;
}
case Opt_discard:
/* if set to 1, even copying files will cause
* trimming :O
* -> user has more control over the online trimming
*/
sbi->minblks_trim = 64;
if (bdev_max_discard_sectors(sb->s_bdev))
*flag |= JFS_DISCARD;
else
pr_err("JFS: discard option not supported on device\n");
break;
case Opt_nodiscard:
*flag &= ~JFS_DISCARD;
break;
case Opt_discard_minblk:
{
char *minblks_trim = args[0].from;
int rc;
if (bdev_max_discard_sectors(sb->s_bdev)) {
*flag |= JFS_DISCARD;
rc = kstrtouint(minblks_trim, 0,
&sbi->minblks_trim);
if (rc)
goto cleanup;
} else
pr_err("JFS: discard option not supported on device\n");
break;
}
default:
printk("jfs: Unrecognized mount option \"%s\" or missing value\n",
p);
goto cleanup;
}
} }
if (nls_map != (void *) -1) {
/* Discard old (if remount) */
unload_nls(sbi->nls_tab);
sbi->nls_tab = nls_map;
}
return 1;
cleanup:
if (nls_map && nls_map != (void *) -1)
unload_nls(nls_map);
return 0; return 0;
} }
static int jfs_remount(struct super_block *sb, int *flags, char *data) static int jfs_reconfigure(struct fs_context *fc)
{ {
s64 newLVSize = 0; struct jfs_context *ctx = fc->fs_private;
struct super_block *sb = fc->root->d_sb;
int readonly = fc->sb_flags & SB_RDONLY;
int rc = 0; int rc = 0;
int flag = JFS_SBI(sb)->flag; int flag = ctx->flag;
int ret; int ret;
sync_filesystem(sb); sync_filesystem(sb);
if (!parse_options(data, sb, &newLVSize, &flag))
return -EINVAL;
if (newLVSize) { /* Transfer results of parsing to the sbi */
JFS_SBI(sb)->flag = ctx->flag;
JFS_SBI(sb)->uid = ctx->uid;
JFS_SBI(sb)->gid = ctx->gid;
JFS_SBI(sb)->umask = ctx->umask;
JFS_SBI(sb)->minblks_trim = ctx->minblks_trim;
if (ctx->nls_map != (void *) -1) {
unload_nls(JFS_SBI(sb)->nls_tab);
JFS_SBI(sb)->nls_tab = ctx->nls_map;
}
ctx->nls_map = NULL;
if (ctx->resize) {
if (sb_rdonly(sb)) { if (sb_rdonly(sb)) {
pr_err("JFS: resize requires volume to be mounted read-write\n"); pr_err("JFS: resize requires volume to be mounted read-write\n");
return -EROFS; return -EROFS;
} }
rc = jfs_extendfs(sb, newLVSize, 0);
if (!ctx->newLVSize) {
ctx->newLVSize = sb_bdev_nr_blocks(sb);
if (ctx->newLVSize == 0)
pr_err("JFS: Cannot determine volume size\n");
}
rc = jfs_extendfs(sb, ctx->newLVSize, 0);
if (rc) if (rc)
return rc; return rc;
} }
if (sb_rdonly(sb) && !(*flags & SB_RDONLY)) { if (sb_rdonly(sb) && !readonly) {
/* /*
* Invalidate any previously read metadata. fsck may have * Invalidate any previously read metadata. fsck may have
* changed the on-disk data since we mounted r/o * changed the on-disk data since we mounted r/o
@ -459,7 +414,7 @@ static int jfs_remount(struct super_block *sb, int *flags, char *data)
dquot_resume(sb, -1); dquot_resume(sb, -1);
return ret; return ret;
} }
if (!sb_rdonly(sb) && (*flags & SB_RDONLY)) { if (!sb_rdonly(sb) && readonly) {
rc = dquot_suspend(sb, -1); rc = dquot_suspend(sb, -1);
if (rc < 0) if (rc < 0)
return rc; return rc;
@ -467,7 +422,7 @@ static int jfs_remount(struct super_block *sb, int *flags, char *data)
JFS_SBI(sb)->flag = flag; JFS_SBI(sb)->flag = flag;
return rc; return rc;
} }
if ((JFS_SBI(sb)->flag & JFS_NOINTEGRITY) != (flag & JFS_NOINTEGRITY)) if ((JFS_SBI(sb)->flag & JFS_NOINTEGRITY) != (flag & JFS_NOINTEGRITY)) {
if (!sb_rdonly(sb)) { if (!sb_rdonly(sb)) {
rc = jfs_umount_rw(sb); rc = jfs_umount_rw(sb);
if (rc) if (rc)
@ -477,18 +432,20 @@ static int jfs_remount(struct super_block *sb, int *flags, char *data)
ret = jfs_mount_rw(sb, 1); ret = jfs_mount_rw(sb, 1);
return ret; return ret;
} }
}
JFS_SBI(sb)->flag = flag; JFS_SBI(sb)->flag = flag;
return 0; return 0;
} }
static int jfs_fill_super(struct super_block *sb, void *data, int silent) static int jfs_fill_super(struct super_block *sb, struct fs_context *fc)
{ {
struct jfs_context *ctx = fc->fs_private;
int silent = fc->sb_flags & SB_SILENT;
struct jfs_sb_info *sbi; struct jfs_sb_info *sbi;
struct inode *inode; struct inode *inode;
int rc; int rc;
s64 newLVSize = 0; int ret = -EINVAL;
int flag, ret = -EINVAL;
jfs_info("In jfs_read_super: s_flags=0x%lx", sb->s_flags); jfs_info("In jfs_read_super: s_flags=0x%lx", sb->s_flags);
@ -501,24 +458,34 @@ static int jfs_fill_super(struct super_block *sb, void *data, int silent)
sb->s_time_min = 0; sb->s_time_min = 0;
sb->s_time_max = U32_MAX; sb->s_time_max = U32_MAX;
sbi->sb = sb; sbi->sb = sb;
sbi->uid = INVALID_UID;
sbi->gid = INVALID_GID;
sbi->umask = -1;
/* initialize the mount flag and determine the default error handler */ /* Transfer results of parsing to the sbi */
flag = JFS_ERR_REMOUNT_RO; sbi->flag = ctx->flag;
sbi->uid = ctx->uid;
sbi->gid = ctx->gid;
sbi->umask = ctx->umask;
if (ctx->nls_map != (void *) -1) {
unload_nls(sbi->nls_tab);
sbi->nls_tab = ctx->nls_map;
}
ctx->nls_map = NULL;
if (!parse_options((char *) data, sb, &newLVSize, &flag)) if (sbi->flag & JFS_DISCARD) {
goto out_kfree; if (!bdev_max_discard_sectors(sb->s_bdev)) {
sbi->flag = flag; pr_err("JFS: discard option not supported on device\n");
sbi->flag &= ~JFS_DISCARD;
} else {
sbi->minblks_trim = ctx->minblks_trim;
}
}
#ifdef CONFIG_JFS_POSIX_ACL #ifdef CONFIG_JFS_POSIX_ACL
sb->s_flags |= SB_POSIXACL; sb->s_flags |= SB_POSIXACL;
#endif #endif
if (newLVSize) { if (ctx->resize) {
pr_err("resize option for remount only\n"); pr_err("resize option for remount only\n");
goto out_kfree; goto out_unload;
} }
/* /*
@ -608,7 +575,6 @@ static int jfs_fill_super(struct super_block *sb, void *data, int silent)
sbi->direct_inode = NULL; sbi->direct_inode = NULL;
out_unload: out_unload:
unload_nls(sbi->nls_tab); unload_nls(sbi->nls_tab);
out_kfree:
kfree(sbi); kfree(sbi);
return ret; return ret;
} }
@ -664,10 +630,9 @@ static int jfs_unfreeze(struct super_block *sb)
return rc; return rc;
} }
static struct dentry *jfs_do_mount(struct file_system_type *fs_type, static int jfs_get_tree(struct fs_context *fc)
int flags, const char *dev_name, void *data)
{ {
return mount_bdev(fs_type, flags, dev_name, data, jfs_fill_super); return get_tree_bdev(fc, jfs_fill_super);
} }
static int jfs_sync_fs(struct super_block *sb, int wait) static int jfs_sync_fs(struct super_block *sb, int wait)
@ -886,7 +851,6 @@ static const struct super_operations jfs_super_operations = {
.freeze_fs = jfs_freeze, .freeze_fs = jfs_freeze,
.unfreeze_fs = jfs_unfreeze, .unfreeze_fs = jfs_unfreeze,
.statfs = jfs_statfs, .statfs = jfs_statfs,
.remount_fs = jfs_remount,
.show_options = jfs_show_options, .show_options = jfs_show_options,
#ifdef CONFIG_QUOTA #ifdef CONFIG_QUOTA
.quota_read = jfs_quota_read, .quota_read = jfs_quota_read,
@ -902,12 +866,71 @@ static const struct export_operations jfs_export_operations = {
.get_parent = jfs_get_parent, .get_parent = jfs_get_parent,
}; };
static void jfs_init_options(struct fs_context *fc, struct jfs_context *ctx)
{
if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
struct super_block *sb = fc->root->d_sb;
/* Copy over current option values and mount flags */
ctx->uid = JFS_SBI(sb)->uid;
ctx->gid = JFS_SBI(sb)->gid;
ctx->umask = JFS_SBI(sb)->umask;
ctx->nls_map = (void *)-1;
ctx->minblks_trim = JFS_SBI(sb)->minblks_trim;
ctx->flag = JFS_SBI(sb)->flag;
} else {
/*
* Initialize the mount flag and determine the default
* error handler
*/
ctx->flag = JFS_ERR_REMOUNT_RO;
ctx->uid = INVALID_UID;
ctx->gid = INVALID_GID;
ctx->umask = -1;
ctx->nls_map = (void *)-1;
}
}
static void jfs_free_fc(struct fs_context *fc)
{
struct jfs_context *ctx = fc->fs_private;
if (ctx->nls_map != (void *) -1)
unload_nls(ctx->nls_map);
kfree(ctx);
}
static const struct fs_context_operations jfs_context_ops = {
.parse_param = jfs_parse_param,
.get_tree = jfs_get_tree,
.reconfigure = jfs_reconfigure,
.free = jfs_free_fc,
};
static int jfs_init_fs_context(struct fs_context *fc)
{
struct jfs_context *ctx;
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
jfs_init_options(fc, ctx);
fc->fs_private = ctx;
fc->ops = &jfs_context_ops;
return 0;
}
static struct file_system_type jfs_fs_type = { static struct file_system_type jfs_fs_type = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "jfs", .name = "jfs",
.mount = jfs_do_mount,
.kill_sb = kill_block_super, .kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV, .fs_flags = FS_REQUIRES_DEV,
.init_fs_context = jfs_init_fs_context,
.parameters = jfs_param_spec,
}; };
MODULE_ALIAS_FS("jfs"); MODULE_ALIAS_FS("jfs");