add patch fix-ext4_show_options-for-fs-wo-journal
[ext4-patch-queue.git] / include-encoding-information-in-superblock
blob3e0b14c289b0023379c139b878e559b2a7b0d15b
1 ext4: include charset encoding information in the superblock
3 From: Gabriel Krisman Bertazi <krisman@collabora.co.uk>
5 Support for encoding is considered an incompatible feature, since it has
6 potential to create collisions of file names in existing filesystems.
7 If the feature flag is not enabled, the entire filesystem will operate
8 on opaque byte sequences, respecting the original behavior.
10 The s_encoding field stores a magic number indicating the encoding
11 format and version used globally by file and directory names in the
12 filesystem.  The s_encoding_flags defines policies for using the charset
13 encoding, like how to handle invalid sequences.  The magic number is
14 mapped to the exact charset table, but the mapping is specific to ext4.
15 Since we don't have any commitment to support old encodings, the only
16 encoding I am supporting right now is utf8-12.1.0.
18 The current implementation prevents the user from enabling encoding and
19 per-directory encryption on the same filesystem at the same time.  The
20 incompatibility between these features lies in how we do efficient
21 directory searches when we cannot be sure the encryption of the user
22 provided fname will match the actual hash stored in the disk without
23 decrypting every directory entry, because of normalization cases.  My
24 quickest solution is to simply block the concurrent use of these
25 features for now, and enable it later, once we have a better solution.
27 Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.co.uk>
28 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
29 ---
30  fs/ext4/ext4.h  | 21 +++++++++++++++++++-
31  fs/ext4/super.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
32  2 files changed, 105 insertions(+), 1 deletion(-)
34 diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
35 index 2a2e6ed9aab4..2219b4f4a593 100644
36 --- a/fs/ext4/ext4.h
37 +++ b/fs/ext4/ext4.h
38 @@ -1313,7 +1313,9 @@ struct ext4_super_block {
39         __u8    s_first_error_time_hi;
40         __u8    s_last_error_time_hi;
41         __u8    s_pad[2];
42 -       __le32  s_reserved[96];         /* Padding to the end of the block */
43 +       __le16  s_encoding;             /* Filename charset encoding */
44 +       __le16  s_encoding_flags;       /* Filename charset encoding flags */
45 +       __le32  s_reserved[95];         /* Padding to the end of the block */
46         __le32  s_checksum;             /* crc32c(superblock) */
47  };
49 @@ -1338,6 +1340,16 @@ struct ext4_super_block {
50  /* Number of quota types we support */
51  #define EXT4_MAXQUOTAS 3
53 +#define EXT4_ENC_UTF8_12_1     1
55 +/*
56 + * Flags for ext4_sb_info.s_encoding_flags.
57 + */
58 +#define EXT4_ENC_STRICT_MODE_FL        (1 << 0)
60 +#define ext4_has_strict_mode(sbi) \
61 +       (sbi->s_encoding_flags & EXT4_ENC_STRICT_MODE_FL)
63  /*
64   * fourth extended-fs super-block data in memory
65   */
66 @@ -1387,6 +1399,10 @@ struct ext4_sb_info {
67         struct kobject s_kobj;
68         struct completion s_kobj_unregister;
69         struct super_block *s_sb;
70 +#ifdef CONFIG_UNICODE
71 +       struct unicode_map *s_encoding;
72 +       __u16 s_encoding_flags;
73 +#endif
75         /* Journaling */
76         struct journal_s *s_journal;
77 @@ -1660,6 +1676,7 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
78  #define EXT4_FEATURE_INCOMPAT_LARGEDIR         0x4000 /* >2GB or 3-lvl htree */
79  #define EXT4_FEATURE_INCOMPAT_INLINE_DATA      0x8000 /* data in inode */
80  #define EXT4_FEATURE_INCOMPAT_ENCRYPT          0x10000
81 +#define EXT4_FEATURE_INCOMPAT_FNAME_ENCODING   0x20000
83  extern void ext4_update_dynamic_rev(struct super_block *sb);
85 @@ -1753,6 +1770,7 @@ EXT4_FEATURE_INCOMPAT_FUNCS(csum_seed,            CSUM_SEED)
86  EXT4_FEATURE_INCOMPAT_FUNCS(largedir,          LARGEDIR)
87  EXT4_FEATURE_INCOMPAT_FUNCS(inline_data,       INLINE_DATA)
88  EXT4_FEATURE_INCOMPAT_FUNCS(encrypt,           ENCRYPT)
89 +EXT4_FEATURE_INCOMPAT_FUNCS(fname_encoding,    FNAME_ENCODING)
91  #define EXT2_FEATURE_COMPAT_SUPP       EXT4_FEATURE_COMPAT_EXT_ATTR
92  #define EXT2_FEATURE_INCOMPAT_SUPP     (EXT4_FEATURE_INCOMPAT_FILETYPE| \
93 @@ -1780,6 +1798,7 @@ EXT4_FEATURE_INCOMPAT_FUNCS(encrypt,              ENCRYPT)
94                                          EXT4_FEATURE_INCOMPAT_MMP | \
95                                          EXT4_FEATURE_INCOMPAT_INLINE_DATA | \
96                                          EXT4_FEATURE_INCOMPAT_ENCRYPT | \
97 +                                        EXT4_FEATURE_INCOMPAT_FNAME_ENCODING | \
98                                          EXT4_FEATURE_INCOMPAT_CSUM_SEED | \
99                                          EXT4_FEATURE_INCOMPAT_LARGEDIR)
100  #define EXT4_FEATURE_RO_COMPAT_SUPP    (EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER| \
101 diff --git a/fs/ext4/super.c b/fs/ext4/super.c
102 index 184944d4d8d1..934814d0a041 100644
103 --- a/fs/ext4/super.c
104 +++ b/fs/ext4/super.c
105 @@ -42,6 +42,7 @@
106  #include <linux/cleancache.h>
107  #include <linux/uaccess.h>
108  #include <linux/iversion.h>
109 +#include <linux/unicode.h>
111  #include <linux/kthread.h>
112  #include <linux/freezer.h>
113 @@ -1054,6 +1055,9 @@ static void ext4_put_super(struct super_block *sb)
114                 crypto_free_shash(sbi->s_chksum_driver);
115         kfree(sbi->s_blockgroup_lock);
116         fs_put_dax(sbi->s_daxdev);
117 +#ifdef CONFIG_UNICODE
118 +       utf8_unload(sbi->s_encoding);
119 +#endif
120         kfree(sbi);
123 @@ -1750,6 +1754,36 @@ static const struct mount_opts {
124         {Opt_err, 0, 0}
125  };
127 +#ifdef CONFIG_UNICODE
128 +static const struct ext4_sb_encodings {
129 +       __u16 magic;
130 +       char *name;
131 +       char *version;
132 +} ext4_sb_encoding_map[] = {
133 +       {EXT4_ENC_UTF8_12_1, "utf8", "12.1.0"},
136 +static int ext4_sb_read_encoding(const struct ext4_super_block *es,
137 +                                const struct ext4_sb_encodings **encoding,
138 +                                __u16 *flags)
140 +       __u16 magic = le16_to_cpu(es->s_encoding);
141 +       int i;
143 +       for (i = 0; i < ARRAY_SIZE(ext4_sb_encoding_map); i++)
144 +               if (magic == ext4_sb_encoding_map[i].magic)
145 +                       break;
147 +       if (i >= ARRAY_SIZE(ext4_sb_encoding_map))
148 +               return -EINVAL;
150 +       *encoding = &ext4_sb_encoding_map[i];
151 +       *flags = le16_to_cpu(es->s_encoding_flags);
153 +       return 0;
155 +#endif
157  static int handle_mount_opt(struct super_block *sb, char *opt, int token,
158                             substring_t *args, unsigned long *journal_devnum,
159                             unsigned int *journal_ioprio, int is_remount)
160 @@ -2880,6 +2914,15 @@ static int ext4_feature_set_ok(struct super_block *sb, int readonly)
161                 return 0;
162         }
164 +#ifndef CONFIG_UNICODE
165 +       if (ext4_has_feature_fname_encoding(sb)) {
166 +               ext4_msg(sb, KERN_ERR,
167 +                        "Filesystem with fname_encoding feature cannot be "
168 +                        "mounted without CONFIG_UNICODE");
169 +               return 0;
170 +       }
171 +#endif
173         if (readonly)
174                 return 1;
176 @@ -3770,6 +3813,43 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
177                            &journal_ioprio, 0))
178                 goto failed_mount;
180 +#ifdef CONFIG_UNICODE
181 +       if (ext4_has_feature_fname_encoding(sb) && !sbi->s_encoding) {
182 +               const struct ext4_sb_encodings *encoding_info;
183 +               struct unicode_map *encoding;
184 +               __u16 encoding_flags;
186 +               if (ext4_has_feature_encrypt(sb)) {
187 +                       ext4_msg(sb, KERN_ERR,
188 +                                "Can't mount with encoding and encryption");
189 +                       goto failed_mount;
190 +               }
192 +               if (ext4_sb_read_encoding(es, &encoding_info,
193 +                                         &encoding_flags)) {
194 +                       ext4_msg(sb, KERN_ERR,
195 +                                "Encoding requested by superblock is unknown");
196 +                       goto failed_mount;
197 +               }
199 +               encoding = utf8_load(encoding_info->version);
200 +               if (IS_ERR(encoding)) {
201 +                       ext4_msg(sb, KERN_ERR,
202 +                                "can't mount with superblock charset: %s-%s "
203 +                                "not supported by the kernel. flags: 0x%x.",
204 +                                encoding_info->name, encoding_info->version,
205 +                                encoding_flags);
206 +                       goto failed_mount;
207 +               }
208 +               ext4_msg(sb, KERN_INFO,"Using encoding defined by superblock: "
209 +                        "%s-%s with flags 0x%hx", encoding_info->name,
210 +                        encoding_info->version?:"\b", encoding_flags);
212 +               sbi->s_encoding = encoding;
213 +               sbi->s_encoding_flags = encoding_flags;
214 +       }
215 +#endif
217         if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA) {
218                 printk_once(KERN_WARNING "EXT4-fs: Warning: mounting "
219                             "with data=journal disables delayed "
220 @@ -4586,6 +4666,11 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
221  failed_mount:
222         if (sbi->s_chksum_driver)
223                 crypto_free_shash(sbi->s_chksum_driver);
225 +#ifdef CONFIG_UNICODE
226 +       utf8_unload(sbi->s_encoding);
227 +#endif
229  #ifdef CONFIG_QUOTA
230         for (i = 0; i < EXT4_MAXQUOTAS; i++)
231                 kfree(sbi->s_qf_names[i]);