Commit ed318a6c authored by Eric Biggers's avatar Eric Biggers

fscrypt: support test_dummy_encryption=v2

v1 encryption policies are deprecated in favor of v2, and some new
features (e.g. encryption+casefolding) are only being added for v2.

Therefore, the "test_dummy_encryption" mount option (which is used for
encryption I/O testing with xfstests) needs to support v2 policies.

To do this, extend its syntax to be "test_dummy_encryption=v1" or
"test_dummy_encryption=v2".  The existing "test_dummy_encryption" (no
argument) also continues to be accepted, to specify the default setting
-- currently v1, but the next patch changes it to v2.

To cleanly support both v1 and v2 while also making it easy to support
specifying other encryption settings in the future (say, accepting
"$contents_mode:$filenames_mode:v2"), make ext4 and f2fs maintain a
pointer to the dummy fscrypt_context rather than using mount flags.

To avoid concurrency issues, don't allow test_dummy_encryption to be set
or changed during a remount.  (The former restriction is new, but
xfstests doesn't run into it, so no one should notice.)

Tested with 'gce-xfstests -c {ext4,f2fs}/encrypt -g auto'.  On ext4,
there are two regressions, both of which are test bugs: ext4/023 and
ext4/028 fail because they set an xattr and expect it to be stored
inline, but the increase in size of the fscrypt_context from
24 to 40 bytes causes this xattr to be spilled into an external block.

Link: https://lore.kernel.org/r/20200512233251.118314-4-ebiggers@kernel.orgAcked-by: default avatarJaegeuk Kim <jaegeuk@kernel.org>
Reviewed-by: default avatarTheodore Ts'o <tytso@mit.edu>
Signed-off-by: default avatarEric Biggers <ebiggers@google.com>
parent cdeb21da
...@@ -225,8 +225,12 @@ fsync_mode=%s Control the policy of fsync. Currently supports "posix", ...@@ -225,8 +225,12 @@ fsync_mode=%s Control the policy of fsync. Currently supports "posix",
pass, but the performance will regress. "nobarrier" is pass, but the performance will regress. "nobarrier" is
based on "posix", but doesn't issue flush command for based on "posix", but doesn't issue flush command for
non-atomic files likewise "nobarrier" mount option. non-atomic files likewise "nobarrier" mount option.
test_dummy_encryption Enable dummy encryption, which provides a fake fscrypt test_dummy_encryption
test_dummy_encryption=%s
Enable dummy encryption, which provides a fake fscrypt
context. The fake fscrypt context is used by xfstests. context. The fake fscrypt context is used by xfstests.
The argument may be either "v1" or "v2", in order to
select the corresponding fscrypt policy version.
checkpoint=%s[:%u[%]] Set to "disable" to turn off checkpointing. Set to "enable" checkpoint=%s[:%u[%]] Set to "disable" to turn off checkpointing. Set to "enable"
to reenable checkpointing. Is enabled by default. While to reenable checkpointing. Is enabled by default. While
disabled, any unmounting or unexpected shutdowns will cause disabled, any unmounting or unexpected shutdowns will cause
......
...@@ -395,21 +395,18 @@ int fscrypt_get_encryption_info(struct inode *inode) ...@@ -395,21 +395,18 @@ int fscrypt_get_encryption_info(struct inode *inode)
res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx)); res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
if (res < 0) { if (res < 0) {
if (!fscrypt_dummy_context_enabled(inode) || const union fscrypt_context *dummy_ctx =
IS_ENCRYPTED(inode)) { fscrypt_get_dummy_context(inode->i_sb);
if (IS_ENCRYPTED(inode) || !dummy_ctx) {
fscrypt_warn(inode, fscrypt_warn(inode,
"Error %d getting encryption context", "Error %d getting encryption context",
res); res);
return res; return res;
} }
/* Fake up a context for an unencrypted directory */ /* Fake up a context for an unencrypted directory */
memset(&ctx, 0, sizeof(ctx)); res = fscrypt_context_size(dummy_ctx);
ctx.version = FSCRYPT_CONTEXT_V1; memcpy(&ctx, dummy_ctx, res);
ctx.v1.contents_encryption_mode = FSCRYPT_MODE_AES_256_XTS;
ctx.v1.filenames_encryption_mode = FSCRYPT_MODE_AES_256_CTS;
memset(ctx.v1.master_key_descriptor, 0x42,
FSCRYPT_KEY_DESCRIPTOR_SIZE);
res = sizeof(ctx.v1);
} }
crypt_info = kmem_cache_zalloc(fscrypt_info_cachep, GFP_NOFS); crypt_info = kmem_cache_zalloc(fscrypt_info_cachep, GFP_NOFS);
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
*/ */
#include <linux/random.h> #include <linux/random.h>
#include <linux/seq_file.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/mount.h> #include <linux/mount.h>
#include "fscrypt_private.h" #include "fscrypt_private.h"
...@@ -616,3 +617,127 @@ int fscrypt_inherit_context(struct inode *parent, struct inode *child, ...@@ -616,3 +617,127 @@ int fscrypt_inherit_context(struct inode *parent, struct inode *child,
return preload ? fscrypt_get_encryption_info(child): 0; return preload ? fscrypt_get_encryption_info(child): 0;
} }
EXPORT_SYMBOL(fscrypt_inherit_context); EXPORT_SYMBOL(fscrypt_inherit_context);
/**
* fscrypt_set_test_dummy_encryption() - handle '-o test_dummy_encryption'
* @sb: the filesystem on which test_dummy_encryption is being specified
* @arg: the argument to the test_dummy_encryption option.
* If no argument was specified, then @arg->from == NULL.
* @dummy_ctx: the filesystem's current dummy context (input/output, see below)
*
* Handle the test_dummy_encryption mount option by creating a dummy encryption
* context, saving it in @dummy_ctx, and adding the corresponding dummy
* encryption key to the filesystem. If the @dummy_ctx is already set, then
* instead validate that it matches @arg. Don't support changing it via
* remount, as that is difficult to do safely.
*
* The reason we use an fscrypt_context rather than an fscrypt_policy is because
* we mustn't generate a new nonce each time we access a dummy-encrypted
* directory, as that would change the way filenames are encrypted.
*
* Return: 0 on success (dummy context set, or the same context is already set);
* -EEXIST if a different dummy context is already set;
* or another -errno value.
*/
int fscrypt_set_test_dummy_encryption(struct super_block *sb,
const substring_t *arg,
struct fscrypt_dummy_context *dummy_ctx)
{
const char *argstr = "v1";
const char *argstr_to_free = NULL;
struct fscrypt_key_specifier key_spec = { 0 };
int version;
union fscrypt_context *ctx = NULL;
int err;
if (arg->from) {
argstr = argstr_to_free = match_strdup(arg);
if (!argstr)
return -ENOMEM;
}
if (!strcmp(argstr, "v1")) {
version = FSCRYPT_CONTEXT_V1;
key_spec.type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR;
memset(key_spec.u.descriptor, 0x42,
FSCRYPT_KEY_DESCRIPTOR_SIZE);
} else if (!strcmp(argstr, "v2")) {
version = FSCRYPT_CONTEXT_V2;
key_spec.type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER;
/* key_spec.u.identifier gets filled in when adding the key */
} else {
err = -EINVAL;
goto out;
}
if (dummy_ctx->ctx) {
/*
* Note: if we ever make test_dummy_encryption support
* specifying other encryption settings, such as the encryption
* modes, we'll need to compare those settings here.
*/
if (dummy_ctx->ctx->version == version)
err = 0;
else
err = -EEXIST;
goto out;
}
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx) {
err = -ENOMEM;
goto out;
}
err = fscrypt_add_test_dummy_key(sb, &key_spec);
if (err)
goto out;
ctx->version = version;
switch (ctx->version) {
case FSCRYPT_CONTEXT_V1:
ctx->v1.contents_encryption_mode = FSCRYPT_MODE_AES_256_XTS;
ctx->v1.filenames_encryption_mode = FSCRYPT_MODE_AES_256_CTS;
memcpy(ctx->v1.master_key_descriptor, key_spec.u.descriptor,
FSCRYPT_KEY_DESCRIPTOR_SIZE);
break;
case FSCRYPT_CONTEXT_V2:
ctx->v2.contents_encryption_mode = FSCRYPT_MODE_AES_256_XTS;
ctx->v2.filenames_encryption_mode = FSCRYPT_MODE_AES_256_CTS;
memcpy(ctx->v2.master_key_identifier, key_spec.u.identifier,
FSCRYPT_KEY_IDENTIFIER_SIZE);
break;
default:
WARN_ON(1);
err = -EINVAL;
goto out;
}
dummy_ctx->ctx = ctx;
ctx = NULL;
err = 0;
out:
kfree(ctx);
kfree(argstr_to_free);
return err;
}
EXPORT_SYMBOL_GPL(fscrypt_set_test_dummy_encryption);
/**
* fscrypt_show_test_dummy_encryption() - show '-o test_dummy_encryption'
* @seq: the seq_file to print the option to
* @sep: the separator character to use
* @sb: the filesystem whose options are being shown
*
* Show the test_dummy_encryption mount option, if it was specified.
* This is mainly used for /proc/mounts.
*/
void fscrypt_show_test_dummy_encryption(struct seq_file *seq, char sep,
struct super_block *sb)
{
const union fscrypt_context *ctx = fscrypt_get_dummy_context(sb);
if (!ctx)
return;
seq_printf(seq, "%ctest_dummy_encryption=v%d", sep, ctx->version);
}
EXPORT_SYMBOL_GPL(fscrypt_show_test_dummy_encryption);
...@@ -1357,11 +1357,9 @@ struct ext4_super_block { ...@@ -1357,11 +1357,9 @@ struct ext4_super_block {
*/ */
#define EXT4_MF_MNTDIR_SAMPLED 0x0001 #define EXT4_MF_MNTDIR_SAMPLED 0x0001
#define EXT4_MF_FS_ABORTED 0x0002 /* Fatal error detected */ #define EXT4_MF_FS_ABORTED 0x0002 /* Fatal error detected */
#define EXT4_MF_TEST_DUMMY_ENCRYPTION 0x0004
#ifdef CONFIG_FS_ENCRYPTION #ifdef CONFIG_FS_ENCRYPTION
#define DUMMY_ENCRYPTION_ENABLED(sbi) (unlikely((sbi)->s_mount_flags & \ #define DUMMY_ENCRYPTION_ENABLED(sbi) ((sbi)->s_dummy_enc_ctx.ctx != NULL)
EXT4_MF_TEST_DUMMY_ENCRYPTION))
#else #else
#define DUMMY_ENCRYPTION_ENABLED(sbi) (0) #define DUMMY_ENCRYPTION_ENABLED(sbi) (0)
#endif #endif
...@@ -1551,6 +1549,9 @@ struct ext4_sb_info { ...@@ -1551,6 +1549,9 @@ struct ext4_sb_info {
struct ratelimit_state s_warning_ratelimit_state; struct ratelimit_state s_warning_ratelimit_state;
struct ratelimit_state s_msg_ratelimit_state; struct ratelimit_state s_msg_ratelimit_state;
/* Encryption context for '-o test_dummy_encryption' */
struct fscrypt_dummy_context s_dummy_enc_ctx;
/* /*
* Barrier between writepages ops and changing any inode's JOURNAL_DATA * Barrier between writepages ops and changing any inode's JOURNAL_DATA
* or EXTENTS flag. * or EXTENTS flag.
......
...@@ -1106,6 +1106,7 @@ static void ext4_put_super(struct super_block *sb) ...@@ -1106,6 +1106,7 @@ static void ext4_put_super(struct super_block *sb)
crypto_free_shash(sbi->s_chksum_driver); crypto_free_shash(sbi->s_chksum_driver);
kfree(sbi->s_blockgroup_lock); kfree(sbi->s_blockgroup_lock);
fs_put_dax(sbi->s_daxdev); fs_put_dax(sbi->s_daxdev);
fscrypt_free_dummy_context(&sbi->s_dummy_enc_ctx);
#ifdef CONFIG_UNICODE #ifdef CONFIG_UNICODE
utf8_unload(sbi->s_encoding); utf8_unload(sbi->s_encoding);
#endif #endif
...@@ -1389,9 +1390,10 @@ static int ext4_set_context(struct inode *inode, const void *ctx, size_t len, ...@@ -1389,9 +1390,10 @@ static int ext4_set_context(struct inode *inode, const void *ctx, size_t len,
return res; return res;
} }
static bool ext4_dummy_context(struct inode *inode) static const union fscrypt_context *
ext4_get_dummy_context(struct super_block *sb)
{ {
return DUMMY_ENCRYPTION_ENABLED(EXT4_SB(inode->i_sb)); return EXT4_SB(sb)->s_dummy_enc_ctx.ctx;
} }
static bool ext4_has_stable_inodes(struct super_block *sb) static bool ext4_has_stable_inodes(struct super_block *sb)
...@@ -1410,7 +1412,7 @@ static const struct fscrypt_operations ext4_cryptops = { ...@@ -1410,7 +1412,7 @@ static const struct fscrypt_operations ext4_cryptops = {
.key_prefix = "ext4:", .key_prefix = "ext4:",
.get_context = ext4_get_context, .get_context = ext4_get_context,
.set_context = ext4_set_context, .set_context = ext4_set_context,
.dummy_context = ext4_dummy_context, .get_dummy_context = ext4_get_dummy_context,
.empty_dir = ext4_empty_dir, .empty_dir = ext4_empty_dir,
.max_namelen = EXT4_NAME_LEN, .max_namelen = EXT4_NAME_LEN,
.has_stable_inodes = ext4_has_stable_inodes, .has_stable_inodes = ext4_has_stable_inodes,
...@@ -1605,6 +1607,7 @@ static const match_table_t tokens = { ...@@ -1605,6 +1607,7 @@ static const match_table_t tokens = {
{Opt_init_itable, "init_itable"}, {Opt_init_itable, "init_itable"},
{Opt_noinit_itable, "noinit_itable"}, {Opt_noinit_itable, "noinit_itable"},
{Opt_max_dir_size_kb, "max_dir_size_kb=%u"}, {Opt_max_dir_size_kb, "max_dir_size_kb=%u"},
{Opt_test_dummy_encryption, "test_dummy_encryption=%s"},
{Opt_test_dummy_encryption, "test_dummy_encryption"}, {Opt_test_dummy_encryption, "test_dummy_encryption"},
{Opt_nombcache, "nombcache"}, {Opt_nombcache, "nombcache"},
{Opt_nombcache, "no_mbcache"}, /* for backward compatibility */ {Opt_nombcache, "no_mbcache"}, /* for backward compatibility */
...@@ -1816,7 +1819,7 @@ static const struct mount_opts { ...@@ -1816,7 +1819,7 @@ static const struct mount_opts {
{Opt_jqfmt_vfsv0, QFMT_VFS_V0, MOPT_QFMT}, {Opt_jqfmt_vfsv0, QFMT_VFS_V0, MOPT_QFMT},
{Opt_jqfmt_vfsv1, QFMT_VFS_V1, MOPT_QFMT}, {Opt_jqfmt_vfsv1, QFMT_VFS_V1, MOPT_QFMT},
{Opt_max_dir_size_kb, 0, MOPT_GTE0}, {Opt_max_dir_size_kb, 0, MOPT_GTE0},
{Opt_test_dummy_encryption, 0, MOPT_GTE0}, {Opt_test_dummy_encryption, 0, MOPT_STRING},
{Opt_nombcache, EXT4_MOUNT_NO_MBCACHE, MOPT_SET}, {Opt_nombcache, EXT4_MOUNT_NO_MBCACHE, MOPT_SET},
{Opt_err, 0, 0} {Opt_err, 0, 0}
}; };
...@@ -1851,6 +1854,48 @@ static int ext4_sb_read_encoding(const struct ext4_super_block *es, ...@@ -1851,6 +1854,48 @@ static int ext4_sb_read_encoding(const struct ext4_super_block *es,
} }
#endif #endif
static int ext4_set_test_dummy_encryption(struct super_block *sb,
const char *opt,
const substring_t *arg,
bool is_remount)
{
#ifdef CONFIG_FS_ENCRYPTION
struct ext4_sb_info *sbi = EXT4_SB(sb);
int err;
/*
* This mount option is just for testing, and it's not worthwhile to
* implement the extra complexity (e.g. RCU protection) that would be
* needed to allow it to be set or changed during remount. We do allow
* it to be specified during remount, but only if there is no change.
*/
if (is_remount && !sbi->s_dummy_enc_ctx.ctx) {
ext4_msg(sb, KERN_WARNING,
"Can't set test_dummy_encryption on remount");
return -1;
}
err = fscrypt_set_test_dummy_encryption(sb, arg, &sbi->s_dummy_enc_ctx);
if (err) {
if (err == -EEXIST)
ext4_msg(sb, KERN_WARNING,
"Can't change test_dummy_encryption on remount");
else if (err == -EINVAL)
ext4_msg(sb, KERN_WARNING,
"Value of option \"%s\" is unrecognized", opt);
else
ext4_msg(sb, KERN_WARNING,
"Error processing option \"%s\" [%d]",
opt, err);
return -1;
}
ext4_msg(sb, KERN_WARNING, "Test dummy encryption mode enabled");
#else
ext4_msg(sb, KERN_WARNING,
"Test dummy encryption mount option ignored");
#endif
return 1;
}
static int handle_mount_opt(struct super_block *sb, char *opt, int token, static int handle_mount_opt(struct super_block *sb, char *opt, int token,
substring_t *args, unsigned long *journal_devnum, substring_t *args, unsigned long *journal_devnum,
unsigned int *journal_ioprio, int is_remount) unsigned int *journal_ioprio, int is_remount)
...@@ -2047,14 +2092,8 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token, ...@@ -2047,14 +2092,8 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token,
*journal_ioprio = *journal_ioprio =
IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, arg); IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, arg);
} else if (token == Opt_test_dummy_encryption) { } else if (token == Opt_test_dummy_encryption) {
#ifdef CONFIG_FS_ENCRYPTION return ext4_set_test_dummy_encryption(sb, opt, &args[0],
sbi->s_mount_flags |= EXT4_MF_TEST_DUMMY_ENCRYPTION; is_remount);
ext4_msg(sb, KERN_WARNING,
"Test dummy encryption mode enabled");
#else
ext4_msg(sb, KERN_WARNING,
"Test dummy encryption mount option ignored");
#endif
} else if (m->flags & MOPT_DATAJ) { } else if (m->flags & MOPT_DATAJ) {
if (is_remount) { if (is_remount) {
if (!sbi->s_journal) if (!sbi->s_journal)
...@@ -2311,8 +2350,8 @@ static int _ext4_show_options(struct seq_file *seq, struct super_block *sb, ...@@ -2311,8 +2350,8 @@ static int _ext4_show_options(struct seq_file *seq, struct super_block *sb,
SEQ_OPTS_PRINT("max_dir_size_kb=%u", sbi->s_max_dir_size_kb); SEQ_OPTS_PRINT("max_dir_size_kb=%u", sbi->s_max_dir_size_kb);
if (test_opt(sb, DATA_ERR_ABORT)) if (test_opt(sb, DATA_ERR_ABORT))
SEQ_OPTS_PUTS("data_err=abort"); SEQ_OPTS_PUTS("data_err=abort");
if (DUMMY_ENCRYPTION_ENABLED(sbi))
SEQ_OPTS_PUTS("test_dummy_encryption"); fscrypt_show_test_dummy_encryption(seq, sep, sb);
ext4_show_quota_options(seq, sb); ext4_show_quota_options(seq, sb);
return 0; return 0;
...@@ -4780,6 +4819,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) ...@@ -4780,6 +4819,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
for (i = 0; i < EXT4_MAXQUOTAS; i++) for (i = 0; i < EXT4_MAXQUOTAS; i++)
kfree(get_qf_name(sb, sbi, i)); kfree(get_qf_name(sb, sbi, i));
#endif #endif
fscrypt_free_dummy_context(&sbi->s_dummy_enc_ctx);
ext4_blkdev_remove(sbi); ext4_blkdev_remove(sbi);
brelse(bh); brelse(bh);
out_fail: out_fail:
......
...@@ -293,6 +293,7 @@ EXT4_ATTR_FEATURE(batched_discard); ...@@ -293,6 +293,7 @@ EXT4_ATTR_FEATURE(batched_discard);
EXT4_ATTR_FEATURE(meta_bg_resize); EXT4_ATTR_FEATURE(meta_bg_resize);
#ifdef CONFIG_FS_ENCRYPTION #ifdef CONFIG_FS_ENCRYPTION
EXT4_ATTR_FEATURE(encryption); EXT4_ATTR_FEATURE(encryption);
EXT4_ATTR_FEATURE(test_dummy_encryption_v2);
#endif #endif
#ifdef CONFIG_UNICODE #ifdef CONFIG_UNICODE
EXT4_ATTR_FEATURE(casefold); EXT4_ATTR_FEATURE(casefold);
...@@ -308,6 +309,7 @@ static struct attribute *ext4_feat_attrs[] = { ...@@ -308,6 +309,7 @@ static struct attribute *ext4_feat_attrs[] = {
ATTR_LIST(meta_bg_resize), ATTR_LIST(meta_bg_resize),
#ifdef CONFIG_FS_ENCRYPTION #ifdef CONFIG_FS_ENCRYPTION
ATTR_LIST(encryption), ATTR_LIST(encryption),
ATTR_LIST(test_dummy_encryption_v2),
#endif #endif
#ifdef CONFIG_UNICODE #ifdef CONFIG_UNICODE
ATTR_LIST(casefold), ATTR_LIST(casefold),
......
...@@ -138,7 +138,7 @@ struct f2fs_mount_info { ...@@ -138,7 +138,7 @@ struct f2fs_mount_info {
int fsync_mode; /* fsync policy */ int fsync_mode; /* fsync policy */
int fs_mode; /* fs mode: LFS or ADAPTIVE */ int fs_mode; /* fs mode: LFS or ADAPTIVE */
int bggc_mode; /* bggc mode: off, on or sync */ int bggc_mode; /* bggc mode: off, on or sync */
bool test_dummy_encryption; /* test dummy encryption */ struct fscrypt_dummy_context dummy_enc_ctx; /* test dummy encryption */
block_t unusable_cap; /* Amount of space allowed to be block_t unusable_cap; /* Amount of space allowed to be
* unusable when disabling checkpoint * unusable when disabling checkpoint
*/ */
...@@ -1259,7 +1259,7 @@ enum fsync_mode { ...@@ -1259,7 +1259,7 @@ enum fsync_mode {
#ifdef CONFIG_FS_ENCRYPTION #ifdef CONFIG_FS_ENCRYPTION
#define DUMMY_ENCRYPTION_ENABLED(sbi) \ #define DUMMY_ENCRYPTION_ENABLED(sbi) \
(unlikely(F2FS_OPTION(sbi).test_dummy_encryption)) (unlikely(F2FS_OPTION(sbi).dummy_enc_ctx.ctx != NULL))
#else #else
#define DUMMY_ENCRYPTION_ENABLED(sbi) (0) #define DUMMY_ENCRYPTION_ENABLED(sbi) (0)
#endif #endif
......
...@@ -202,6 +202,7 @@ static match_table_t f2fs_tokens = { ...@@ -202,6 +202,7 @@ static match_table_t f2fs_tokens = {
{Opt_whint, "whint_mode=%s"}, {Opt_whint, "whint_mode=%s"},
{Opt_alloc, "alloc_mode=%s"}, {Opt_alloc, "alloc_mode=%s"},
{Opt_fsync, "fsync_mode=%s"}, {Opt_fsync, "fsync_mode=%s"},
{Opt_test_dummy_encryption, "test_dummy_encryption=%s"},
{Opt_test_dummy_encryption, "test_dummy_encryption"}, {Opt_test_dummy_encryption, "test_dummy_encryption"},
{Opt_checkpoint_disable, "checkpoint=disable"}, {Opt_checkpoint_disable, "checkpoint=disable"},
{Opt_checkpoint_disable_cap, "checkpoint=disable:%u"}, {Opt_checkpoint_disable_cap, "checkpoint=disable:%u"},
...@@ -394,7 +395,52 @@ static int f2fs_check_quota_options(struct f2fs_sb_info *sbi) ...@@ -394,7 +395,52 @@ static int f2fs_check_quota_options(struct f2fs_sb_info *sbi)
} }
#endif #endif
static int parse_options(struct super_block *sb, char *options) static int f2fs_set_test_dummy_encryption(struct super_block *sb,
const char *opt,
const substring_t *arg,
bool is_remount)
{
struct f2fs_sb_info *sbi = F2FS_SB(sb);
#ifdef CONFIG_FS_ENCRYPTION
int err;
if (!f2fs_sb_has_encrypt(sbi)) {
f2fs_err(sbi, "Encrypt feature is off");
return -EINVAL;
}
/*
* This mount option is just for testing, and it's not worthwhile to
* implement the extra complexity (e.g. RCU protection) that would be
* needed to allow it to be set or changed during remount. We do allow
* it to be specified during remount, but only if there is no change.
*/
if (is_remount && !F2FS_OPTION(sbi).dummy_enc_ctx.ctx) {
f2fs_warn(sbi, "Can't set test_dummy_encryption on remount");
return -EINVAL;
}
err = fscrypt_set_test_dummy_encryption(
sb, arg, &F2FS_OPTION(sbi).dummy_enc_ctx);
if (err) {
if (err == -EEXIST)
f2fs_warn(sbi,
"Can't change test_dummy_encryption on remount");
else if (err == -EINVAL)
f2fs_warn(sbi, "Value of option \"%s\" is unrecognized",
opt);
else
f2fs_warn(sbi, "Error processing option \"%s\" [%d]",
opt, err);
return -EINVAL;
}
f2fs_warn(sbi, "Test dummy encryption mode enabled");
#else
f2fs_warn(sbi, "Test dummy encryption mount option ignored");
#endif
return 0;
}
static int parse_options(struct super_block *sb, char *options, bool is_remount)
{ {
struct f2fs_sb_info *sbi = F2FS_SB(sb); struct f2fs_sb_info *sbi = F2FS_SB(sb);
substring_t args[MAX_OPT_ARGS]; substring_t args[MAX_OPT_ARGS];
...@@ -403,9 +449,7 @@ static int parse_options(struct super_block *sb, char *options) ...@@ -403,9 +449,7 @@ static int parse_options(struct super_block *sb, char *options)
int arg = 0, ext_cnt; int arg = 0, ext_cnt;
kuid_t uid; kuid_t uid;
kgid_t gid; kgid_t gid;
#ifdef CONFIG_QUOTA
int ret; int ret;
#endif
if (!options) if (!options)
return 0; return 0;
...@@ -778,17 +822,10 @@ static int parse_options(struct super_block *sb, char *options) ...@@ -778,17 +822,10 @@ static int parse_options(struct super_block *sb, char *options)
kvfree(name); kvfree(name);
break; break;
case Opt_test_dummy_encryption: case Opt_test_dummy_encryption:
#ifdef CONFIG_FS_ENCRYPTION ret = f2fs_set_test_dummy_encryption(sb, p, &args[0],
if (!f2fs_sb_has_encrypt(sbi)) { is_remount);
f2fs_err(sbi, "Encrypt feature is off"); if (ret)
return -EINVAL; return ret;
}
F2FS_OPTION(sbi).test_dummy_encryption = true;
f2fs_info(sbi, "Test dummy encryption mode enabled");
#else
f2fs_info(sbi, "Test dummy encryption mount option ignored");
#endif
break; break;
case Opt_checkpoint_disable_cap_perc: case Opt_checkpoint_disable_cap_perc:
if (args->from && match_int(args, &arg)) if (args->from && match_int(args, &arg))
...@@ -1213,6 +1250,7 @@ static void f2fs_put_super(struct super_block *sb) ...@@ -1213,6 +1250,7 @@ static void f2fs_put_super(struct super_block *sb)
for (i = 0; i < MAXQUOTAS; i++) for (i = 0; i < MAXQUOTAS; i++)
kvfree(F2FS_OPTION(sbi).s_qf_names[i]); kvfree(F2FS_OPTION(sbi).s_qf_names[i]);
#endif #endif
fscrypt_free_dummy_context(&F2FS_OPTION(sbi).dummy_enc_ctx);
destroy_percpu_info(sbi); destroy_percpu_info(sbi);
for (i = 0; i < NR_PAGE_TYPE; i++) for (i = 0; i < NR_PAGE_TYPE; i++)
kvfree(sbi->write_io[i]); kvfree(sbi->write_io[i]);
...@@ -1543,10 +1581,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) ...@@ -1543,10 +1581,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
seq_printf(seq, ",whint_mode=%s", "user-based"); seq_printf(seq, ",whint_mode=%s", "user-based");
else if (F2FS_OPTION(sbi).whint_mode == WHINT_MODE_FS) else if (F2FS_OPTION(sbi).whint_mode == WHINT_MODE_FS)
seq_printf(seq, ",whint_mode=%s", "fs-based"); seq_printf(seq, ",whint_mode=%s", "fs-based");
#ifdef CONFIG_FS_ENCRYPTION
if (F2FS_OPTION(sbi).test_dummy_encryption) fscrypt_show_test_dummy_encryption(seq, ',', sbi->sb);
seq_puts(seq, ",test_dummy_encryption");
#endif
if (F2FS_OPTION(sbi).alloc_mode == ALLOC_MODE_DEFAULT) if (F2FS_OPTION(sbi).alloc_mode == ALLOC_MODE_DEFAULT)
seq_printf(seq, ",alloc_mode=%s", "default"); seq_printf(seq, ",alloc_mode=%s", "default");
...@@ -1575,7 +1611,6 @@ static void default_options(struct f2fs_sb_info *sbi) ...@@ -1575,7 +1611,6 @@ static void default_options(struct f2fs_sb_info *sbi)
F2FS_OPTION(sbi).whint_mode = WHINT_MODE_OFF; F2FS_OPTION(sbi).whint_mode = WHINT_MODE_OFF;
F2FS_OPTION(sbi).alloc_mode = ALLOC_MODE_DEFAULT; F2FS_OPTION(sbi).alloc_mode = ALLOC_MODE_DEFAULT;
F2FS_OPTION(sbi).fsync_mode = FSYNC_MODE_POSIX; F2FS_OPTION(sbi).fsync_mode = FSYNC_MODE_POSIX;
F2FS_OPTION(sbi).test_dummy_encryption = false;
F2FS_OPTION(sbi).s_resuid = make_kuid(&init_user_ns, F2FS_DEF_RESUID); F2FS_OPTION(sbi).s_resuid = make_kuid(&init_user_ns, F2FS_DEF_RESUID);
F2FS_OPTION(sbi).s_resgid = make_kgid(&init_user_ns, F2FS_DEF_RESGID); F2FS_OPTION(sbi).s_resgid = make_kgid(&init_user_ns, F2FS_DEF_RESGID);
F2FS_OPTION(sbi).compress_algorithm = COMPRESS_LZ4; F2FS_OPTION(sbi).compress_algorithm = COMPRESS_LZ4;
...@@ -1734,7 +1769,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) ...@@ -1734,7 +1769,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
default_options(sbi); default_options(sbi);
/* parse mount options */ /* parse mount options */
err = parse_options(sb, data); err = parse_options(sb, data, true);
if (err) if (err)
goto restore_opts; goto restore_opts;
checkpoint_changed = checkpoint_changed =
...@@ -2410,9 +2445,10 @@ static int f2fs_set_context(struct inode *inode, const void *ctx, size_t len, ...@@ -2410,9 +2445,10 @@ static int f2fs_set_context(struct inode *inode, const void *ctx, size_t len,
ctx, len, fs_data, XATTR_CREATE); ctx, len, fs_data, XATTR_CREATE);
} }
static bool f2fs_dummy_context(struct inode *inode) static const union fscrypt_context *
f2fs_get_dummy_context(struct super_block *sb)
{ {
return DUMMY_ENCRYPTION_ENABLED(F2FS_I_SB(inode)); return F2FS_OPTION(F2FS_SB(sb)).dummy_enc_ctx.ctx;
} }
static bool f2fs_has_stable_inodes(struct super_block *sb) static bool f2fs_has_stable_inodes(struct super_block *sb)
...@@ -2431,7 +2467,7 @@ static const struct fscrypt_operations f2fs_cryptops = { ...@@ -2431,7 +2467,7 @@ static const struct fscrypt_operations f2fs_cryptops = {
.key_prefix = "f2fs:", .key_prefix = "f2fs:",
.get_context = f2fs_get_context, .get_context = f2fs_get_context,
.set_context = f2fs_set_context, .set_context = f2fs_set_context,
.dummy_context = f2fs_dummy_context, .get_dummy_context = f2fs_get_dummy_context,
.empty_dir = f2fs_empty_dir, .empty_dir = f2fs_empty_dir,
.max_namelen = F2FS_NAME_LEN, .max_namelen = F2FS_NAME_LEN,
.has_stable_inodes = f2fs_has_stable_inodes, .has_stable_inodes = f2fs_has_stable_inodes,
...@@ -3366,7 +3402,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) ...@@ -3366,7 +3402,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
goto free_sb_buf; goto free_sb_buf;
} }
err = parse_options(sb, options); err = parse_options(sb, options, false);
if (err) if (err)
goto free_options; goto free_options;
...@@ -3769,6 +3805,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) ...@@ -3769,6 +3805,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
for (i = 0; i < MAXQUOTAS; i++) for (i = 0; i < MAXQUOTAS; i++)
kvfree(F2FS_OPTION(sbi).s_qf_names[i]); kvfree(F2FS_OPTION(sbi).s_qf_names[i]);
#endif #endif
fscrypt_free_dummy_context(&F2FS_OPTION(sbi).dummy_enc_ctx);
kvfree(options); kvfree(options);
free_sb_buf: free_sb_buf:
kvfree(raw_super); kvfree(raw_super);
......
...@@ -446,6 +446,7 @@ enum feat_id { ...@@ -446,6 +446,7 @@ enum feat_id {
FEAT_SB_CHECKSUM, FEAT_SB_CHECKSUM,
FEAT_CASEFOLD, FEAT_CASEFOLD,
FEAT_COMPRESSION, FEAT_COMPRESSION,
FEAT_TEST_DUMMY_ENCRYPTION_V2,
}; };
static ssize_t f2fs_feature_show(struct f2fs_attr *a, static ssize_t f2fs_feature_show(struct f2fs_attr *a,
...@@ -466,6 +467,7 @@ static ssize_t f2fs_feature_show(struct f2fs_attr *a, ...@@ -466,6 +467,7 @@ static ssize_t f2fs_feature_show(struct f2fs_attr *a,
case FEAT_SB_CHECKSUM: case FEAT_SB_CHECKSUM:
case FEAT_CASEFOLD: case FEAT_CASEFOLD:
case FEAT_COMPRESSION: case FEAT_COMPRESSION:
case FEAT_TEST_DUMMY_ENCRYPTION_V2:
return sprintf(buf, "supported\n"); return sprintf(buf, "supported\n");
} }
return 0; return 0;
...@@ -563,6 +565,7 @@ F2FS_GENERAL_RO_ATTR(avg_vblocks); ...@@ -563,6 +565,7 @@ F2FS_GENERAL_RO_ATTR(avg_vblocks);
#ifdef CONFIG_FS_ENCRYPTION #ifdef CONFIG_FS_ENCRYPTION
F2FS_FEATURE_RO_ATTR(encryption, FEAT_CRYPTO); F2FS_FEATURE_RO_ATTR(encryption, FEAT_CRYPTO);
F2FS_FEATURE_RO_ATTR(test_dummy_encryption_v2, FEAT_TEST_DUMMY_ENCRYPTION_V2);
#endif #endif
#ifdef CONFIG_BLK_DEV_ZONED #ifdef CONFIG_BLK_DEV_ZONED
F2FS_FEATURE_RO_ATTR(block_zoned, FEAT_BLKZONED); F2FS_FEATURE_RO_ATTR(block_zoned, FEAT_BLKZONED);
...@@ -647,6 +650,7 @@ ATTRIBUTE_GROUPS(f2fs); ...@@ -647,6 +650,7 @@ ATTRIBUTE_GROUPS(f2fs);
static struct attribute *f2fs_feat_attrs[] = { static struct attribute *f2fs_feat_attrs[] = {
#ifdef CONFIG_FS_ENCRYPTION #ifdef CONFIG_FS_ENCRYPTION
ATTR_LIST(encryption), ATTR_LIST(encryption),
ATTR_LIST(test_dummy_encryption_v2),
#endif #endif
#ifdef CONFIG_BLK_DEV_ZONED #ifdef CONFIG_BLK_DEV_ZONED
ATTR_LIST(block_zoned), ATTR_LIST(block_zoned),
......
...@@ -15,12 +15,15 @@ ...@@ -15,12 +15,15 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/parser.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <uapi/linux/fscrypt.h> #include <uapi/linux/fscrypt.h>
#define FS_CRYPTO_BLOCK_SIZE 16 #define FS_CRYPTO_BLOCK_SIZE 16
union fscrypt_context;
struct fscrypt_info; struct fscrypt_info;
struct seq_file;
struct fscrypt_str { struct fscrypt_str {
unsigned char *name; unsigned char *name;
...@@ -59,7 +62,8 @@ struct fscrypt_operations { ...@@ -59,7 +62,8 @@ struct fscrypt_operations {
int (*get_context)(struct inode *inode, void *ctx, size_t len); int (*get_context)(struct inode *inode, void *ctx, size_t len);
int (*set_context)(struct inode *inode, const void *ctx, size_t len, int (*set_context)(struct inode *inode, const void *ctx, size_t len,
void *fs_data); void *fs_data);
bool (*dummy_context)(struct inode *inode); const union fscrypt_context *(*get_dummy_context)(
struct super_block *sb);
bool (*empty_dir)(struct inode *inode); bool (*empty_dir)(struct inode *inode);
unsigned int max_namelen; unsigned int max_namelen;
bool (*has_stable_inodes)(struct super_block *sb); bool (*has_stable_inodes)(struct super_block *sb);
...@@ -89,10 +93,12 @@ static inline bool fscrypt_needs_contents_encryption(const struct inode *inode) ...@@ -89,10 +93,12 @@ static inline bool fscrypt_needs_contents_encryption(const struct inode *inode)
return IS_ENCRYPTED(inode) && S_ISREG(inode->i_mode); return IS_ENCRYPTED(inode) && S_ISREG(inode->i_mode);
} }
static inline bool fscrypt_dummy_context_enabled(struct inode *inode) static inline const union fscrypt_context *
fscrypt_get_dummy_context(struct super_block *sb)
{ {
return inode->i_sb->s_cop->dummy_context && if (!sb->s_cop->get_dummy_context)
inode->i_sb->s_cop->dummy_context(inode); return NULL;
return sb->s_cop->get_dummy_context(sb);
} }
/* /*
...@@ -145,6 +151,22 @@ int fscrypt_has_permitted_context(struct inode *parent, struct inode *child); ...@@ -145,6 +151,22 @@ int fscrypt_has_permitted_context(struct inode *parent, struct inode *child);
int fscrypt_inherit_context(struct inode *parent, struct inode *child, int fscrypt_inherit_context(struct inode *parent, struct inode *child,
void *fs_data, bool preload); void *fs_data, bool preload);
struct fscrypt_dummy_context {
const union fscrypt_context *ctx;
};
int fscrypt_set_test_dummy_encryption(struct super_block *sb,
const substring_t *arg,
struct fscrypt_dummy_context *dummy_ctx);
void fscrypt_show_test_dummy_encryption(struct seq_file *seq, char sep,
struct super_block *sb);
static inline void
fscrypt_free_dummy_context(struct fscrypt_dummy_context *dummy_ctx)
{
kfree(dummy_ctx->ctx);
dummy_ctx->ctx = NULL;
}
/* keyring.c */ /* keyring.c */
void fscrypt_sb_free(struct super_block *sb); void fscrypt_sb_free(struct super_block *sb);
int fscrypt_ioctl_add_key(struct file *filp, void __user *arg); int fscrypt_ioctl_add_key(struct file *filp, void __user *arg);
...@@ -219,9 +241,10 @@ static inline bool fscrypt_needs_contents_encryption(const struct inode *inode) ...@@ -219,9 +241,10 @@ static inline bool fscrypt_needs_contents_encryption(const struct inode *inode)
return false; return false;
} }
static inline bool fscrypt_dummy_context_enabled(struct inode *inode) static inline const union fscrypt_context *
fscrypt_get_dummy_context(struct super_block *sb)
{ {
return false; return NULL;
} }
static inline void fscrypt_handle_d_move(struct dentry *dentry) static inline void fscrypt_handle_d_move(struct dentry *dentry)
...@@ -316,6 +339,20 @@ static inline int fscrypt_inherit_context(struct inode *parent, ...@@ -316,6 +339,20 @@ static inline int fscrypt_inherit_context(struct inode *parent,
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
struct fscrypt_dummy_context {
};
static inline void fscrypt_show_test_dummy_encryption(struct seq_file *seq,
char sep,
struct super_block *sb)
{
}
static inline void
fscrypt_free_dummy_context(struct fscrypt_dummy_context *dummy_ctx)
{
}
/* keyring.c */ /* keyring.c */
static inline void fscrypt_sb_free(struct super_block *sb) static inline void fscrypt_sb_free(struct super_block *sb)
{ {
...@@ -677,7 +714,7 @@ static inline int fscrypt_prepare_symlink(struct inode *dir, ...@@ -677,7 +714,7 @@ static inline int fscrypt_prepare_symlink(struct inode *dir,
unsigned int max_len, unsigned int max_len,
struct fscrypt_str *disk_link) struct fscrypt_str *disk_link)
{ {
if (IS_ENCRYPTED(dir) || fscrypt_dummy_context_enabled(dir)) if (IS_ENCRYPTED(dir) || fscrypt_get_dummy_context(dir->i_sb) != NULL)
return __fscrypt_prepare_symlink(dir, len, max_len, disk_link); return __fscrypt_prepare_symlink(dir, len, max_len, disk_link);
disk_link->name = (unsigned char *)target; disk_link->name = (unsigned char *)target;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment