Skip to content

Commit c83ad55

Browse files
Gabriel Krisman Bertazitytso
authored andcommitted
ext4: include charset encoding information in the superblock
Support for encoding is considered an incompatible feature, since it has potential to create collisions of file names in existing filesystems. If the feature flag is not enabled, the entire filesystem will operate on opaque byte sequences, respecting the original behavior. The s_encoding field stores a magic number indicating the encoding format and version used globally by file and directory names in the filesystem. The s_encoding_flags defines policies for using the charset encoding, like how to handle invalid sequences. The magic number is mapped to the exact charset table, but the mapping is specific to ext4. Since we don't have any commitment to support old encodings, the only encoding I am supporting right now is utf8-12.1.0. The current implementation prevents the user from enabling encoding and per-directory encryption on the same filesystem at the same time. The incompatibility between these features lies in how we do efficient directory searches when we cannot be sure the encryption of the user provided fname will match the actual hash stored in the disk without decrypting every directory entry, because of normalization cases. My quickest solution is to simply block the concurrent use of these features for now, and enable it later, once we have a better solution. Signed-off-by: Gabriel Krisman Bertazi <[email protected]> Signed-off-by: Theodore Ts'o <[email protected]>
1 parent e765b4a commit c83ad55

File tree

2 files changed

+105
-1
lines changed

2 files changed

+105
-1
lines changed

fs/ext4/ext4.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1313,7 +1313,9 @@ struct ext4_super_block {
13131313
__u8 s_first_error_time_hi;
13141314
__u8 s_last_error_time_hi;
13151315
__u8 s_pad[2];
1316-
__le32 s_reserved[96]; /* Padding to the end of the block */
1316+
__le16 s_encoding; /* Filename charset encoding */
1317+
__le16 s_encoding_flags; /* Filename charset encoding flags */
1318+
__le32 s_reserved[95]; /* Padding to the end of the block */
13171319
__le32 s_checksum; /* crc32c(superblock) */
13181320
};
13191321

@@ -1338,6 +1340,16 @@ struct ext4_super_block {
13381340
/* Number of quota types we support */
13391341
#define EXT4_MAXQUOTAS 3
13401342

1343+
#define EXT4_ENC_UTF8_12_1 1
1344+
1345+
/*
1346+
* Flags for ext4_sb_info.s_encoding_flags.
1347+
*/
1348+
#define EXT4_ENC_STRICT_MODE_FL (1 << 0)
1349+
1350+
#define ext4_has_strict_mode(sbi) \
1351+
(sbi->s_encoding_flags & EXT4_ENC_STRICT_MODE_FL)
1352+
13411353
/*
13421354
* fourth extended-fs super-block data in memory
13431355
*/
@@ -1387,6 +1399,10 @@ struct ext4_sb_info {
13871399
struct kobject s_kobj;
13881400
struct completion s_kobj_unregister;
13891401
struct super_block *s_sb;
1402+
#ifdef CONFIG_UNICODE
1403+
struct unicode_map *s_encoding;
1404+
__u16 s_encoding_flags;
1405+
#endif
13901406

13911407
/* Journaling */
13921408
struct journal_s *s_journal;
@@ -1660,6 +1676,7 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
16601676
#define EXT4_FEATURE_INCOMPAT_LARGEDIR 0x4000 /* >2GB or 3-lvl htree */
16611677
#define EXT4_FEATURE_INCOMPAT_INLINE_DATA 0x8000 /* data in inode */
16621678
#define EXT4_FEATURE_INCOMPAT_ENCRYPT 0x10000
1679+
#define EXT4_FEATURE_INCOMPAT_CASEFOLD 0x20000
16631680

16641681
extern void ext4_update_dynamic_rev(struct super_block *sb);
16651682

@@ -1753,6 +1770,7 @@ EXT4_FEATURE_INCOMPAT_FUNCS(csum_seed, CSUM_SEED)
17531770
EXT4_FEATURE_INCOMPAT_FUNCS(largedir, LARGEDIR)
17541771
EXT4_FEATURE_INCOMPAT_FUNCS(inline_data, INLINE_DATA)
17551772
EXT4_FEATURE_INCOMPAT_FUNCS(encrypt, ENCRYPT)
1773+
EXT4_FEATURE_INCOMPAT_FUNCS(casefold, CASEFOLD)
17561774

17571775
#define EXT2_FEATURE_COMPAT_SUPP EXT4_FEATURE_COMPAT_EXT_ATTR
17581776
#define EXT2_FEATURE_INCOMPAT_SUPP (EXT4_FEATURE_INCOMPAT_FILETYPE| \
@@ -1780,6 +1798,7 @@ EXT4_FEATURE_INCOMPAT_FUNCS(encrypt, ENCRYPT)
17801798
EXT4_FEATURE_INCOMPAT_MMP | \
17811799
EXT4_FEATURE_INCOMPAT_INLINE_DATA | \
17821800
EXT4_FEATURE_INCOMPAT_ENCRYPT | \
1801+
EXT4_FEATURE_INCOMPAT_CASEFOLD | \
17831802
EXT4_FEATURE_INCOMPAT_CSUM_SEED | \
17841803
EXT4_FEATURE_INCOMPAT_LARGEDIR)
17851804
#define EXT4_FEATURE_RO_COMPAT_SUPP (EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER| \

fs/ext4/super.c

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include <linux/cleancache.h>
4343
#include <linux/uaccess.h>
4444
#include <linux/iversion.h>
45+
#include <linux/unicode.h>
4546

4647
#include <linux/kthread.h>
4748
#include <linux/freezer.h>
@@ -1054,6 +1055,9 @@ static void ext4_put_super(struct super_block *sb)
10541055
crypto_free_shash(sbi->s_chksum_driver);
10551056
kfree(sbi->s_blockgroup_lock);
10561057
fs_put_dax(sbi->s_daxdev);
1058+
#ifdef CONFIG_UNICODE
1059+
utf8_unload(sbi->s_encoding);
1060+
#endif
10571061
kfree(sbi);
10581062
}
10591063

@@ -1750,6 +1754,36 @@ static const struct mount_opts {
17501754
{Opt_err, 0, 0}
17511755
};
17521756

1757+
#ifdef CONFIG_UNICODE
1758+
static const struct ext4_sb_encodings {
1759+
__u16 magic;
1760+
char *name;
1761+
char *version;
1762+
} ext4_sb_encoding_map[] = {
1763+
{EXT4_ENC_UTF8_12_1, "utf8", "12.1.0"},
1764+
};
1765+
1766+
static int ext4_sb_read_encoding(const struct ext4_super_block *es,
1767+
const struct ext4_sb_encodings **encoding,
1768+
__u16 *flags)
1769+
{
1770+
__u16 magic = le16_to_cpu(es->s_encoding);
1771+
int i;
1772+
1773+
for (i = 0; i < ARRAY_SIZE(ext4_sb_encoding_map); i++)
1774+
if (magic == ext4_sb_encoding_map[i].magic)
1775+
break;
1776+
1777+
if (i >= ARRAY_SIZE(ext4_sb_encoding_map))
1778+
return -EINVAL;
1779+
1780+
*encoding = &ext4_sb_encoding_map[i];
1781+
*flags = le16_to_cpu(es->s_encoding_flags);
1782+
1783+
return 0;
1784+
}
1785+
#endif
1786+
17531787
static int handle_mount_opt(struct super_block *sb, char *opt, int token,
17541788
substring_t *args, unsigned long *journal_devnum,
17551789
unsigned int *journal_ioprio, int is_remount)
@@ -2880,6 +2914,15 @@ static int ext4_feature_set_ok(struct super_block *sb, int readonly)
28802914
return 0;
28812915
}
28822916

2917+
#ifndef CONFIG_UNICODE
2918+
if (ext4_has_feature_casefold(sb)) {
2919+
ext4_msg(sb, KERN_ERR,
2920+
"Filesystem with casefold feature cannot be "
2921+
"mounted without CONFIG_UNICODE");
2922+
return 0;
2923+
}
2924+
#endif
2925+
28832926
if (readonly)
28842927
return 1;
28852928

@@ -3770,6 +3813,43 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
37703813
&journal_ioprio, 0))
37713814
goto failed_mount;
37723815

3816+
#ifdef CONFIG_UNICODE
3817+
if (ext4_has_feature_casefold(sb) && !sbi->s_encoding) {
3818+
const struct ext4_sb_encodings *encoding_info;
3819+
struct unicode_map *encoding;
3820+
__u16 encoding_flags;
3821+
3822+
if (ext4_has_feature_encrypt(sb)) {
3823+
ext4_msg(sb, KERN_ERR,
3824+
"Can't mount with encoding and encryption");
3825+
goto failed_mount;
3826+
}
3827+
3828+
if (ext4_sb_read_encoding(es, &encoding_info,
3829+
&encoding_flags)) {
3830+
ext4_msg(sb, KERN_ERR,
3831+
"Encoding requested by superblock is unknown");
3832+
goto failed_mount;
3833+
}
3834+
3835+
encoding = utf8_load(encoding_info->version);
3836+
if (IS_ERR(encoding)) {
3837+
ext4_msg(sb, KERN_ERR,
3838+
"can't mount with superblock charset: %s-%s "
3839+
"not supported by the kernel. flags: 0x%x.",
3840+
encoding_info->name, encoding_info->version,
3841+
encoding_flags);
3842+
goto failed_mount;
3843+
}
3844+
ext4_msg(sb, KERN_INFO,"Using encoding defined by superblock: "
3845+
"%s-%s with flags 0x%hx", encoding_info->name,
3846+
encoding_info->version?:"\b", encoding_flags);
3847+
3848+
sbi->s_encoding = encoding;
3849+
sbi->s_encoding_flags = encoding_flags;
3850+
}
3851+
#endif
3852+
37733853
if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA) {
37743854
printk_once(KERN_WARNING "EXT4-fs: Warning: mounting "
37753855
"with data=journal disables delayed "
@@ -4586,6 +4666,11 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
45864666
failed_mount:
45874667
if (sbi->s_chksum_driver)
45884668
crypto_free_shash(sbi->s_chksum_driver);
4669+
4670+
#ifdef CONFIG_UNICODE
4671+
utf8_unload(sbi->s_encoding);
4672+
#endif
4673+
45894674
#ifdef CONFIG_QUOTA
45904675
for (i = 0; i < EXT4_MAXQUOTAS; i++)
45914676
kfree(sbi->s_qf_names[i]);

0 commit comments

Comments
 (0)