Skip to content

Commit ed318a6

Browse files
committed
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/[email protected] Acked-by: Jaegeuk Kim <[email protected]> Reviewed-by: Theodore Ts'o <[email protected]> Signed-off-by: Eric Biggers <[email protected]>
1 parent cdeb21d commit ed318a6

File tree

10 files changed

+307
-60
lines changed

10 files changed

+307
-60
lines changed

Documentation/filesystems/f2fs.rst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,12 @@ fsync_mode=%s Control the policy of fsync. Currently supports "posix",
225225
pass, but the performance will regress. "nobarrier" is
226226
based on "posix", but doesn't issue flush command for
227227
non-atomic files likewise "nobarrier" mount option.
228-
test_dummy_encryption Enable dummy encryption, which provides a fake fscrypt
228+
test_dummy_encryption
229+
test_dummy_encryption=%s
230+
Enable dummy encryption, which provides a fake fscrypt
229231
context. The fake fscrypt context is used by xfstests.
232+
The argument may be either "v1" or "v2", in order to
233+
select the corresponding fscrypt policy version.
230234
checkpoint=%s[:%u[%]] Set to "disable" to turn off checkpointing. Set to "enable"
231235
to reenable checkpointing. Is enabled by default. While
232236
disabled, any unmounting or unexpected shutdowns will cause

fs/crypto/keysetup.c

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -395,21 +395,18 @@ int fscrypt_get_encryption_info(struct inode *inode)
395395

396396
res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
397397
if (res < 0) {
398-
if (!fscrypt_dummy_context_enabled(inode) ||
399-
IS_ENCRYPTED(inode)) {
398+
const union fscrypt_context *dummy_ctx =
399+
fscrypt_get_dummy_context(inode->i_sb);
400+
401+
if (IS_ENCRYPTED(inode) || !dummy_ctx) {
400402
fscrypt_warn(inode,
401403
"Error %d getting encryption context",
402404
res);
403405
return res;
404406
}
405407
/* Fake up a context for an unencrypted directory */
406-
memset(&ctx, 0, sizeof(ctx));
407-
ctx.version = FSCRYPT_CONTEXT_V1;
408-
ctx.v1.contents_encryption_mode = FSCRYPT_MODE_AES_256_XTS;
409-
ctx.v1.filenames_encryption_mode = FSCRYPT_MODE_AES_256_CTS;
410-
memset(ctx.v1.master_key_descriptor, 0x42,
411-
FSCRYPT_KEY_DESCRIPTOR_SIZE);
412-
res = sizeof(ctx.v1);
408+
res = fscrypt_context_size(dummy_ctx);
409+
memcpy(&ctx, dummy_ctx, res);
413410
}
414411

415412
crypt_info = kmem_cache_zalloc(fscrypt_info_cachep, GFP_NOFS);

fs/crypto/policy.c

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
*/
1212

1313
#include <linux/random.h>
14+
#include <linux/seq_file.h>
1415
#include <linux/string.h>
1516
#include <linux/mount.h>
1617
#include "fscrypt_private.h"
@@ -616,3 +617,127 @@ int fscrypt_inherit_context(struct inode *parent, struct inode *child,
616617
return preload ? fscrypt_get_encryption_info(child): 0;
617618
}
618619
EXPORT_SYMBOL(fscrypt_inherit_context);
620+
621+
/**
622+
* fscrypt_set_test_dummy_encryption() - handle '-o test_dummy_encryption'
623+
* @sb: the filesystem on which test_dummy_encryption is being specified
624+
* @arg: the argument to the test_dummy_encryption option.
625+
* If no argument was specified, then @arg->from == NULL.
626+
* @dummy_ctx: the filesystem's current dummy context (input/output, see below)
627+
*
628+
* Handle the test_dummy_encryption mount option by creating a dummy encryption
629+
* context, saving it in @dummy_ctx, and adding the corresponding dummy
630+
* encryption key to the filesystem. If the @dummy_ctx is already set, then
631+
* instead validate that it matches @arg. Don't support changing it via
632+
* remount, as that is difficult to do safely.
633+
*
634+
* The reason we use an fscrypt_context rather than an fscrypt_policy is because
635+
* we mustn't generate a new nonce each time we access a dummy-encrypted
636+
* directory, as that would change the way filenames are encrypted.
637+
*
638+
* Return: 0 on success (dummy context set, or the same context is already set);
639+
* -EEXIST if a different dummy context is already set;
640+
* or another -errno value.
641+
*/
642+
int fscrypt_set_test_dummy_encryption(struct super_block *sb,
643+
const substring_t *arg,
644+
struct fscrypt_dummy_context *dummy_ctx)
645+
{
646+
const char *argstr = "v1";
647+
const char *argstr_to_free = NULL;
648+
struct fscrypt_key_specifier key_spec = { 0 };
649+
int version;
650+
union fscrypt_context *ctx = NULL;
651+
int err;
652+
653+
if (arg->from) {
654+
argstr = argstr_to_free = match_strdup(arg);
655+
if (!argstr)
656+
return -ENOMEM;
657+
}
658+
659+
if (!strcmp(argstr, "v1")) {
660+
version = FSCRYPT_CONTEXT_V1;
661+
key_spec.type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR;
662+
memset(key_spec.u.descriptor, 0x42,
663+
FSCRYPT_KEY_DESCRIPTOR_SIZE);
664+
} else if (!strcmp(argstr, "v2")) {
665+
version = FSCRYPT_CONTEXT_V2;
666+
key_spec.type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER;
667+
/* key_spec.u.identifier gets filled in when adding the key */
668+
} else {
669+
err = -EINVAL;
670+
goto out;
671+
}
672+
673+
if (dummy_ctx->ctx) {
674+
/*
675+
* Note: if we ever make test_dummy_encryption support
676+
* specifying other encryption settings, such as the encryption
677+
* modes, we'll need to compare those settings here.
678+
*/
679+
if (dummy_ctx->ctx->version == version)
680+
err = 0;
681+
else
682+
err = -EEXIST;
683+
goto out;
684+
}
685+
686+
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
687+
if (!ctx) {
688+
err = -ENOMEM;
689+
goto out;
690+
}
691+
692+
err = fscrypt_add_test_dummy_key(sb, &key_spec);
693+
if (err)
694+
goto out;
695+
696+
ctx->version = version;
697+
switch (ctx->version) {
698+
case FSCRYPT_CONTEXT_V1:
699+
ctx->v1.contents_encryption_mode = FSCRYPT_MODE_AES_256_XTS;
700+
ctx->v1.filenames_encryption_mode = FSCRYPT_MODE_AES_256_CTS;
701+
memcpy(ctx->v1.master_key_descriptor, key_spec.u.descriptor,
702+
FSCRYPT_KEY_DESCRIPTOR_SIZE);
703+
break;
704+
case FSCRYPT_CONTEXT_V2:
705+
ctx->v2.contents_encryption_mode = FSCRYPT_MODE_AES_256_XTS;
706+
ctx->v2.filenames_encryption_mode = FSCRYPT_MODE_AES_256_CTS;
707+
memcpy(ctx->v2.master_key_identifier, key_spec.u.identifier,
708+
FSCRYPT_KEY_IDENTIFIER_SIZE);
709+
break;
710+
default:
711+
WARN_ON(1);
712+
err = -EINVAL;
713+
goto out;
714+
}
715+
dummy_ctx->ctx = ctx;
716+
ctx = NULL;
717+
err = 0;
718+
out:
719+
kfree(ctx);
720+
kfree(argstr_to_free);
721+
return err;
722+
}
723+
EXPORT_SYMBOL_GPL(fscrypt_set_test_dummy_encryption);
724+
725+
/**
726+
* fscrypt_show_test_dummy_encryption() - show '-o test_dummy_encryption'
727+
* @seq: the seq_file to print the option to
728+
* @sep: the separator character to use
729+
* @sb: the filesystem whose options are being shown
730+
*
731+
* Show the test_dummy_encryption mount option, if it was specified.
732+
* This is mainly used for /proc/mounts.
733+
*/
734+
void fscrypt_show_test_dummy_encryption(struct seq_file *seq, char sep,
735+
struct super_block *sb)
736+
{
737+
const union fscrypt_context *ctx = fscrypt_get_dummy_context(sb);
738+
739+
if (!ctx)
740+
return;
741+
seq_printf(seq, "%ctest_dummy_encryption=v%d", sep, ctx->version);
742+
}
743+
EXPORT_SYMBOL_GPL(fscrypt_show_test_dummy_encryption);

fs/ext4/ext4.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1357,11 +1357,9 @@ struct ext4_super_block {
13571357
*/
13581358
#define EXT4_MF_MNTDIR_SAMPLED 0x0001
13591359
#define EXT4_MF_FS_ABORTED 0x0002 /* Fatal error detected */
1360-
#define EXT4_MF_TEST_DUMMY_ENCRYPTION 0x0004
13611360

13621361
#ifdef CONFIG_FS_ENCRYPTION
1363-
#define DUMMY_ENCRYPTION_ENABLED(sbi) (unlikely((sbi)->s_mount_flags & \
1364-
EXT4_MF_TEST_DUMMY_ENCRYPTION))
1362+
#define DUMMY_ENCRYPTION_ENABLED(sbi) ((sbi)->s_dummy_enc_ctx.ctx != NULL)
13651363
#else
13661364
#define DUMMY_ENCRYPTION_ENABLED(sbi) (0)
13671365
#endif
@@ -1551,6 +1549,9 @@ struct ext4_sb_info {
15511549
struct ratelimit_state s_warning_ratelimit_state;
15521550
struct ratelimit_state s_msg_ratelimit_state;
15531551

1552+
/* Encryption context for '-o test_dummy_encryption' */
1553+
struct fscrypt_dummy_context s_dummy_enc_ctx;
1554+
15541555
/*
15551556
* Barrier between writepages ops and changing any inode's JOURNAL_DATA
15561557
* or EXTENTS flag.

fs/ext4/super.c

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,6 +1106,7 @@ static void ext4_put_super(struct super_block *sb)
11061106
crypto_free_shash(sbi->s_chksum_driver);
11071107
kfree(sbi->s_blockgroup_lock);
11081108
fs_put_dax(sbi->s_daxdev);
1109+
fscrypt_free_dummy_context(&sbi->s_dummy_enc_ctx);
11091110
#ifdef CONFIG_UNICODE
11101111
utf8_unload(sbi->s_encoding);
11111112
#endif
@@ -1389,9 +1390,10 @@ static int ext4_set_context(struct inode *inode, const void *ctx, size_t len,
13891390
return res;
13901391
}
13911392

1392-
static bool ext4_dummy_context(struct inode *inode)
1393+
static const union fscrypt_context *
1394+
ext4_get_dummy_context(struct super_block *sb)
13931395
{
1394-
return DUMMY_ENCRYPTION_ENABLED(EXT4_SB(inode->i_sb));
1396+
return EXT4_SB(sb)->s_dummy_enc_ctx.ctx;
13951397
}
13961398

13971399
static bool ext4_has_stable_inodes(struct super_block *sb)
@@ -1410,7 +1412,7 @@ static const struct fscrypt_operations ext4_cryptops = {
14101412
.key_prefix = "ext4:",
14111413
.get_context = ext4_get_context,
14121414
.set_context = ext4_set_context,
1413-
.dummy_context = ext4_dummy_context,
1415+
.get_dummy_context = ext4_get_dummy_context,
14141416
.empty_dir = ext4_empty_dir,
14151417
.max_namelen = EXT4_NAME_LEN,
14161418
.has_stable_inodes = ext4_has_stable_inodes,
@@ -1605,6 +1607,7 @@ static const match_table_t tokens = {
16051607
{Opt_init_itable, "init_itable"},
16061608
{Opt_noinit_itable, "noinit_itable"},
16071609
{Opt_max_dir_size_kb, "max_dir_size_kb=%u"},
1610+
{Opt_test_dummy_encryption, "test_dummy_encryption=%s"},
16081611
{Opt_test_dummy_encryption, "test_dummy_encryption"},
16091612
{Opt_nombcache, "nombcache"},
16101613
{Opt_nombcache, "no_mbcache"}, /* for backward compatibility */
@@ -1816,7 +1819,7 @@ static const struct mount_opts {
18161819
{Opt_jqfmt_vfsv0, QFMT_VFS_V0, MOPT_QFMT},
18171820
{Opt_jqfmt_vfsv1, QFMT_VFS_V1, MOPT_QFMT},
18181821
{Opt_max_dir_size_kb, 0, MOPT_GTE0},
1819-
{Opt_test_dummy_encryption, 0, MOPT_GTE0},
1822+
{Opt_test_dummy_encryption, 0, MOPT_STRING},
18201823
{Opt_nombcache, EXT4_MOUNT_NO_MBCACHE, MOPT_SET},
18211824
{Opt_err, 0, 0}
18221825
};
@@ -1851,6 +1854,48 @@ static int ext4_sb_read_encoding(const struct ext4_super_block *es,
18511854
}
18521855
#endif
18531856

1857+
static int ext4_set_test_dummy_encryption(struct super_block *sb,
1858+
const char *opt,
1859+
const substring_t *arg,
1860+
bool is_remount)
1861+
{
1862+
#ifdef CONFIG_FS_ENCRYPTION
1863+
struct ext4_sb_info *sbi = EXT4_SB(sb);
1864+
int err;
1865+
1866+
/*
1867+
* This mount option is just for testing, and it's not worthwhile to
1868+
* implement the extra complexity (e.g. RCU protection) that would be
1869+
* needed to allow it to be set or changed during remount. We do allow
1870+
* it to be specified during remount, but only if there is no change.
1871+
*/
1872+
if (is_remount && !sbi->s_dummy_enc_ctx.ctx) {
1873+
ext4_msg(sb, KERN_WARNING,
1874+
"Can't set test_dummy_encryption on remount");
1875+
return -1;
1876+
}
1877+
err = fscrypt_set_test_dummy_encryption(sb, arg, &sbi->s_dummy_enc_ctx);
1878+
if (err) {
1879+
if (err == -EEXIST)
1880+
ext4_msg(sb, KERN_WARNING,
1881+
"Can't change test_dummy_encryption on remount");
1882+
else if (err == -EINVAL)
1883+
ext4_msg(sb, KERN_WARNING,
1884+
"Value of option \"%s\" is unrecognized", opt);
1885+
else
1886+
ext4_msg(sb, KERN_WARNING,
1887+
"Error processing option \"%s\" [%d]",
1888+
opt, err);
1889+
return -1;
1890+
}
1891+
ext4_msg(sb, KERN_WARNING, "Test dummy encryption mode enabled");
1892+
#else
1893+
ext4_msg(sb, KERN_WARNING,
1894+
"Test dummy encryption mount option ignored");
1895+
#endif
1896+
return 1;
1897+
}
1898+
18541899
static int handle_mount_opt(struct super_block *sb, char *opt, int token,
18551900
substring_t *args, unsigned long *journal_devnum,
18561901
unsigned int *journal_ioprio, int is_remount)
@@ -2047,14 +2092,8 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token,
20472092
*journal_ioprio =
20482093
IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, arg);
20492094
} else if (token == Opt_test_dummy_encryption) {
2050-
#ifdef CONFIG_FS_ENCRYPTION
2051-
sbi->s_mount_flags |= EXT4_MF_TEST_DUMMY_ENCRYPTION;
2052-
ext4_msg(sb, KERN_WARNING,
2053-
"Test dummy encryption mode enabled");
2054-
#else
2055-
ext4_msg(sb, KERN_WARNING,
2056-
"Test dummy encryption mount option ignored");
2057-
#endif
2095+
return ext4_set_test_dummy_encryption(sb, opt, &args[0],
2096+
is_remount);
20582097
} else if (m->flags & MOPT_DATAJ) {
20592098
if (is_remount) {
20602099
if (!sbi->s_journal)
@@ -2311,8 +2350,8 @@ static int _ext4_show_options(struct seq_file *seq, struct super_block *sb,
23112350
SEQ_OPTS_PRINT("max_dir_size_kb=%u", sbi->s_max_dir_size_kb);
23122351
if (test_opt(sb, DATA_ERR_ABORT))
23132352
SEQ_OPTS_PUTS("data_err=abort");
2314-
if (DUMMY_ENCRYPTION_ENABLED(sbi))
2315-
SEQ_OPTS_PUTS("test_dummy_encryption");
2353+
2354+
fscrypt_show_test_dummy_encryption(seq, sep, sb);
23162355

23172356
ext4_show_quota_options(seq, sb);
23182357
return 0;
@@ -4780,6 +4819,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
47804819
for (i = 0; i < EXT4_MAXQUOTAS; i++)
47814820
kfree(get_qf_name(sb, sbi, i));
47824821
#endif
4822+
fscrypt_free_dummy_context(&sbi->s_dummy_enc_ctx);
47834823
ext4_blkdev_remove(sbi);
47844824
brelse(bh);
47854825
out_fail:

fs/ext4/sysfs.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ EXT4_ATTR_FEATURE(batched_discard);
293293
EXT4_ATTR_FEATURE(meta_bg_resize);
294294
#ifdef CONFIG_FS_ENCRYPTION
295295
EXT4_ATTR_FEATURE(encryption);
296+
EXT4_ATTR_FEATURE(test_dummy_encryption_v2);
296297
#endif
297298
#ifdef CONFIG_UNICODE
298299
EXT4_ATTR_FEATURE(casefold);
@@ -308,6 +309,7 @@ static struct attribute *ext4_feat_attrs[] = {
308309
ATTR_LIST(meta_bg_resize),
309310
#ifdef CONFIG_FS_ENCRYPTION
310311
ATTR_LIST(encryption),
312+
ATTR_LIST(test_dummy_encryption_v2),
311313
#endif
312314
#ifdef CONFIG_UNICODE
313315
ATTR_LIST(casefold),

fs/f2fs/f2fs.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ struct f2fs_mount_info {
138138
int fsync_mode; /* fsync policy */
139139
int fs_mode; /* fs mode: LFS or ADAPTIVE */
140140
int bggc_mode; /* bggc mode: off, on or sync */
141-
bool test_dummy_encryption; /* test dummy encryption */
141+
struct fscrypt_dummy_context dummy_enc_ctx; /* test dummy encryption */
142142
block_t unusable_cap; /* Amount of space allowed to be
143143
* unusable when disabling checkpoint
144144
*/
@@ -1259,7 +1259,7 @@ enum fsync_mode {
12591259

12601260
#ifdef CONFIG_FS_ENCRYPTION
12611261
#define DUMMY_ENCRYPTION_ENABLED(sbi) \
1262-
(unlikely(F2FS_OPTION(sbi).test_dummy_encryption))
1262+
(unlikely(F2FS_OPTION(sbi).dummy_enc_ctx.ctx != NULL))
12631263
#else
12641264
#define DUMMY_ENCRYPTION_ENABLED(sbi) (0)
12651265
#endif

0 commit comments

Comments
 (0)