Skip to content

Commit 5abe379

Browse files
committed
Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4
Pull ext4 updates from Ted Ts'o: "Add as a feature case-insensitive directories (the casefold feature) using Unicode 12.1. Also, the usual largish number of cleanups and bug fixes" * tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (25 commits) ext4: export /sys/fs/ext4/feature/casefold if Unicode support is present ext4: fix ext4_show_options for file systems w/o journal unicode: refactor the rule for regenerating utf8data.h docs: ext4.rst: document case-insensitive directories ext4: Support case-insensitive file name lookups ext4: include charset encoding information in the superblock MAINTAINERS: add Unicode subsystem entry unicode: update unicode database unicode version 12.1.0 unicode: introduce test module for normalized utf8 implementation unicode: implement higher level API for string handling unicode: reduce the size of utf8data[] unicode: introduce code for UTF-8 normalization unicode: introduce UTF-8 character database ext4: actually request zeroing of inode table after grow ext4: cond_resched in work-heavy group loops ext4: fix use-after-free race with debug_want_extra_isize ext4: avoid drop reference to iloc.bh twice ext4: ignore e_value_offs for xattrs with value-in-ea-inode ext4: protect journal inode's blocks using block_validity ext4: use BUG() instead of BUG_ON(1) ...
2 parents e5fef2a + db90f41 commit 5abe379

35 files changed

+9591
-59
lines changed

Documentation/admin-guide/ext4.rst

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,48 @@ Currently Available
9191
* large block (up to pagesize) support
9292
* efficient new ordered mode in JBD2 and ext4 (avoid using buffer head to force
9393
the ordering)
94+
* Case-insensitive file name lookups
9495

9596
[1] Filesystems with a block size of 1k may see a limit imposed by the
9697
directory hash tree having a maximum depth of two.
9798

99+
case-insensitive file name lookups
100+
======================================================
101+
102+
The case-insensitive file name lookup feature is supported on a
103+
per-directory basis, allowing the user to mix case-insensitive and
104+
case-sensitive directories in the same filesystem. It is enabled by
105+
flipping the +F inode attribute of an empty directory. The
106+
case-insensitive string match operation is only defined when we know how
107+
text in encoded in a byte sequence. For that reason, in order to enable
108+
case-insensitive directories, the filesystem must have the
109+
casefold feature, which stores the filesystem-wide encoding
110+
model used. By default, the charset adopted is the latest version of
111+
Unicode (12.1.0, by the time of this writing), encoded in the UTF-8
112+
form. The comparison algorithm is implemented by normalizing the
113+
strings to the Canonical decomposition form, as defined by Unicode,
114+
followed by a byte per byte comparison.
115+
116+
The case-awareness is name-preserving on the disk, meaning that the file
117+
name provided by userspace is a byte-per-byte match to what is actually
118+
written in the disk. The Unicode normalization format used by the
119+
kernel is thus an internal representation, and not exposed to the
120+
userspace nor to the disk, with the important exception of disk hashes,
121+
used on large case-insensitive directories with DX feature. On DX
122+
directories, the hash must be calculated using the casefolded version of
123+
the filename, meaning that the normalization format used actually has an
124+
impact on where the directory entry is stored.
125+
126+
When we change from viewing filenames as opaque byte sequences to seeing
127+
them as encoded strings we need to address what happens when a program
128+
tries to create a file with an invalid name. The Unicode subsystem
129+
within the kernel leaves the decision of what to do in this case to the
130+
filesystem, which select its preferred behavior by enabling/disabling
131+
the strict mode. When Ext4 encounters one of those strings and the
132+
filesystem did not require strict mode, it falls back to considering the
133+
entire string as an opaque byte sequence, which still allows the user to
134+
operate on that file, but the case-insensitive lookups won't work.
135+
98136
Options
99137
=======
100138

Documentation/dontdiff

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ mkprep
176176
mkregtable
177177
mktables
178178
mktree
179+
mkutf8data
179180
modpost
180181
modules.builtin
181182
modules.order
@@ -254,6 +255,7 @@ vsyscall_32.lds
254255
wanxlfw.inc
255256
uImage
256257
unifdef
258+
utf8data.h
257259
wakeup.bin
258260
wakeup.elf
259261
wakeup.lds

MAINTAINERS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15984,6 +15984,12 @@ F: drivers/uwb/
1598415984
F: include/linux/uwb.h
1598515985
F: include/linux/uwb/
1598615986

15987+
UNICODE SUBSYSTEM:
15988+
M: Gabriel Krisman Bertazi <[email protected]>
15989+
15990+
S: Supported
15991+
F: fs/unicode/
15992+
1598715993
UNICORE32 ARCHITECTURE:
1598815994
M: Guan Xuetao <[email protected]>
1598915995
W: http://mprc.pku.edu.cn/~guanxuetao/linux

fs/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,5 +317,6 @@ endif # NETWORK_FILESYSTEMS
317317

318318
source "fs/nls/Kconfig"
319319
source "fs/dlm/Kconfig"
320+
source "fs/unicode/Kconfig"
320321

321322
endmenu

fs/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ obj-$(CONFIG_EXPORTFS) += exportfs/
9292
obj-$(CONFIG_NFSD) += nfsd/
9393
obj-$(CONFIG_LOCKD) += lockd/
9494
obj-$(CONFIG_NLS) += nls/
95+
obj-$(CONFIG_UNICODE) += unicode/
9596
obj-$(CONFIG_SYSV_FS) += sysv/
9697
obj-$(CONFIG_CIFS) += cifs/
9798
obj-$(CONFIG_HPFS_FS) += hpfs/

fs/ext4/block_validity.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,48 @@ static void debug_print_tree(struct ext4_sb_info *sbi)
137137
printk(KERN_CONT "\n");
138138
}
139139

140+
static int ext4_protect_reserved_inode(struct super_block *sb, u32 ino)
141+
{
142+
struct inode *inode;
143+
struct ext4_sb_info *sbi = EXT4_SB(sb);
144+
struct ext4_map_blocks map;
145+
u32 i = 0, err = 0, num, n;
146+
147+
if ((ino < EXT4_ROOT_INO) ||
148+
(ino > le32_to_cpu(sbi->s_es->s_inodes_count)))
149+
return -EINVAL;
150+
inode = ext4_iget(sb, ino, EXT4_IGET_SPECIAL);
151+
if (IS_ERR(inode))
152+
return PTR_ERR(inode);
153+
num = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
154+
while (i < num) {
155+
map.m_lblk = i;
156+
map.m_len = num - i;
157+
n = ext4_map_blocks(NULL, inode, &map, 0);
158+
if (n < 0) {
159+
err = n;
160+
break;
161+
}
162+
if (n == 0) {
163+
i++;
164+
} else {
165+
if (!ext4_data_block_valid(sbi, map.m_pblk, n)) {
166+
ext4_error(sb, "blocks %llu-%llu from inode %u "
167+
"overlap system zone", map.m_pblk,
168+
map.m_pblk + map.m_len - 1, ino);
169+
err = -EFSCORRUPTED;
170+
break;
171+
}
172+
err = add_system_zone(sbi, map.m_pblk, n);
173+
if (err < 0)
174+
break;
175+
i += n;
176+
}
177+
}
178+
iput(inode);
179+
return err;
180+
}
181+
140182
int ext4_setup_system_zone(struct super_block *sb)
141183
{
142184
ext4_group_t ngroups = ext4_get_groups_count(sb);
@@ -155,6 +197,7 @@ int ext4_setup_system_zone(struct super_block *sb)
155197
return 0;
156198

157199
for (i=0; i < ngroups; i++) {
200+
cond_resched();
158201
if (ext4_bg_has_super(sb, i) &&
159202
((i < 5) || ((i % flex_size) == 0)))
160203
add_system_zone(sbi, ext4_group_first_block_no(sb, i),
@@ -171,6 +214,12 @@ int ext4_setup_system_zone(struct super_block *sb)
171214
if (ret)
172215
return ret;
173216
}
217+
if (ext4_has_feature_journal(sb) && sbi->s_es->s_journal_inum) {
218+
ret = ext4_protect_reserved_inode(sb,
219+
le32_to_cpu(sbi->s_es->s_journal_inum));
220+
if (ret)
221+
return ret;
222+
}
174223

175224
if (test_opt(sb, DEBUG))
176225
debug_print_tree(sbi);

fs/ext4/dir.c

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <linux/buffer_head.h>
2727
#include <linux/slab.h>
2828
#include <linux/iversion.h>
29+
#include <linux/unicode.h>
2930
#include "ext4.h"
3031
#include "xattr.h"
3132

@@ -660,3 +661,50 @@ const struct file_operations ext4_dir_operations = {
660661
.open = ext4_dir_open,
661662
.release = ext4_release_dir,
662663
};
664+
665+
#ifdef CONFIG_UNICODE
666+
static int ext4_d_compare(const struct dentry *dentry, unsigned int len,
667+
const char *str, const struct qstr *name)
668+
{
669+
struct qstr qstr = {.name = str, .len = len };
670+
671+
if (!IS_CASEFOLDED(dentry->d_parent->d_inode)) {
672+
if (len != name->len)
673+
return -1;
674+
return !memcmp(str, name, len);
675+
}
676+
677+
return ext4_ci_compare(dentry->d_parent->d_inode, name, &qstr);
678+
}
679+
680+
static int ext4_d_hash(const struct dentry *dentry, struct qstr *str)
681+
{
682+
const struct ext4_sb_info *sbi = EXT4_SB(dentry->d_sb);
683+
const struct unicode_map *um = sbi->s_encoding;
684+
unsigned char *norm;
685+
int len, ret = 0;
686+
687+
if (!IS_CASEFOLDED(dentry->d_inode))
688+
return 0;
689+
690+
norm = kmalloc(PATH_MAX, GFP_ATOMIC);
691+
if (!norm)
692+
return -ENOMEM;
693+
694+
len = utf8_casefold(um, str, norm, PATH_MAX);
695+
if (len < 0) {
696+
if (ext4_has_strict_mode(sbi))
697+
ret = -EINVAL;
698+
goto out;
699+
}
700+
str->hash = full_name_hash(dentry, norm, len);
701+
out:
702+
kfree(norm);
703+
return ret;
704+
}
705+
706+
const struct dentry_operations ext4_dentry_ops = {
707+
.d_hash = ext4_d_hash,
708+
.d_compare = ext4_d_compare,
709+
};
710+
#endif

fs/ext4/ext4.h

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -399,10 +399,11 @@ struct flex_groups {
399399
#define EXT4_EOFBLOCKS_FL 0x00400000 /* Blocks allocated beyond EOF */
400400
#define EXT4_INLINE_DATA_FL 0x10000000 /* Inode has inline data. */
401401
#define EXT4_PROJINHERIT_FL 0x20000000 /* Create with parents projid */
402+
#define EXT4_CASEFOLD_FL 0x40000000 /* Casefolded file */
402403
#define EXT4_RESERVED_FL 0x80000000 /* reserved for ext4 lib */
403404

404-
#define EXT4_FL_USER_VISIBLE 0x304BDFFF /* User visible flags */
405-
#define EXT4_FL_USER_MODIFIABLE 0x204BC0FF /* User modifiable flags */
405+
#define EXT4_FL_USER_VISIBLE 0x704BDFFF /* User visible flags */
406+
#define EXT4_FL_USER_MODIFIABLE 0x604BC0FF /* User modifiable flags */
406407

407408
/* Flags we can manipulate with through EXT4_IOC_FSSETXATTR */
408409
#define EXT4_FL_XFLAG_VISIBLE (EXT4_SYNC_FL | \
@@ -417,10 +418,10 @@ struct flex_groups {
417418
EXT4_SYNC_FL | EXT4_NODUMP_FL | EXT4_NOATIME_FL |\
418419
EXT4_NOCOMPR_FL | EXT4_JOURNAL_DATA_FL |\
419420
EXT4_NOTAIL_FL | EXT4_DIRSYNC_FL |\
420-
EXT4_PROJINHERIT_FL)
421+
EXT4_PROJINHERIT_FL | EXT4_CASEFOLD_FL)
421422

422423
/* Flags that are appropriate for regular files (all but dir-specific ones). */
423-
#define EXT4_REG_FLMASK (~(EXT4_DIRSYNC_FL | EXT4_TOPDIR_FL))
424+
#define EXT4_REG_FLMASK (~(EXT4_DIRSYNC_FL | EXT4_TOPDIR_FL | EXT4_CASEFOLD_FL))
424425

425426
/* Flags that are appropriate for non-directories/regular files. */
426427
#define EXT4_OTHER_FLMASK (EXT4_NODUMP_FL | EXT4_NOATIME_FL)
@@ -1313,7 +1314,9 @@ struct ext4_super_block {
13131314
__u8 s_first_error_time_hi;
13141315
__u8 s_last_error_time_hi;
13151316
__u8 s_pad[2];
1316-
__le32 s_reserved[96]; /* Padding to the end of the block */
1317+
__le16 s_encoding; /* Filename charset encoding */
1318+
__le16 s_encoding_flags; /* Filename charset encoding flags */
1319+
__le32 s_reserved[95]; /* Padding to the end of the block */
13171320
__le32 s_checksum; /* crc32c(superblock) */
13181321
};
13191322

@@ -1338,6 +1341,16 @@ struct ext4_super_block {
13381341
/* Number of quota types we support */
13391342
#define EXT4_MAXQUOTAS 3
13401343

1344+
#define EXT4_ENC_UTF8_12_1 1
1345+
1346+
/*
1347+
* Flags for ext4_sb_info.s_encoding_flags.
1348+
*/
1349+
#define EXT4_ENC_STRICT_MODE_FL (1 << 0)
1350+
1351+
#define ext4_has_strict_mode(sbi) \
1352+
(sbi->s_encoding_flags & EXT4_ENC_STRICT_MODE_FL)
1353+
13411354
/*
13421355
* fourth extended-fs super-block data in memory
13431356
*/
@@ -1387,6 +1400,10 @@ struct ext4_sb_info {
13871400
struct kobject s_kobj;
13881401
struct completion s_kobj_unregister;
13891402
struct super_block *s_sb;
1403+
#ifdef CONFIG_UNICODE
1404+
struct unicode_map *s_encoding;
1405+
__u16 s_encoding_flags;
1406+
#endif
13901407

13911408
/* Journaling */
13921409
struct journal_s *s_journal;
@@ -1592,9 +1609,6 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
15921609
#define EXT4_SB(sb) (sb)
15931610
#endif
15941611

1595-
/*
1596-
* Returns true if the inode is inode is encrypted
1597-
*/
15981612
#define NEXT_ORPHAN(inode) EXT4_I(inode)->i_dtime
15991613

16001614
/*
@@ -1663,6 +1677,7 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
16631677
#define EXT4_FEATURE_INCOMPAT_LARGEDIR 0x4000 /* >2GB or 3-lvl htree */
16641678
#define EXT4_FEATURE_INCOMPAT_INLINE_DATA 0x8000 /* data in inode */
16651679
#define EXT4_FEATURE_INCOMPAT_ENCRYPT 0x10000
1680+
#define EXT4_FEATURE_INCOMPAT_CASEFOLD 0x20000
16661681

16671682
extern void ext4_update_dynamic_rev(struct super_block *sb);
16681683

@@ -1756,6 +1771,7 @@ EXT4_FEATURE_INCOMPAT_FUNCS(csum_seed, CSUM_SEED)
17561771
EXT4_FEATURE_INCOMPAT_FUNCS(largedir, LARGEDIR)
17571772
EXT4_FEATURE_INCOMPAT_FUNCS(inline_data, INLINE_DATA)
17581773
EXT4_FEATURE_INCOMPAT_FUNCS(encrypt, ENCRYPT)
1774+
EXT4_FEATURE_INCOMPAT_FUNCS(casefold, CASEFOLD)
17591775

17601776
#define EXT2_FEATURE_COMPAT_SUPP EXT4_FEATURE_COMPAT_EXT_ATTR
17611777
#define EXT2_FEATURE_INCOMPAT_SUPP (EXT4_FEATURE_INCOMPAT_FILETYPE| \
@@ -1783,6 +1799,7 @@ EXT4_FEATURE_INCOMPAT_FUNCS(encrypt, ENCRYPT)
17831799
EXT4_FEATURE_INCOMPAT_MMP | \
17841800
EXT4_FEATURE_INCOMPAT_INLINE_DATA | \
17851801
EXT4_FEATURE_INCOMPAT_ENCRYPT | \
1802+
EXT4_FEATURE_INCOMPAT_CASEFOLD | \
17861803
EXT4_FEATURE_INCOMPAT_CSUM_SEED | \
17871804
EXT4_FEATURE_INCOMPAT_LARGEDIR)
17881805
#define EXT4_FEATURE_RO_COMPAT_SUPP (EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER| \
@@ -2376,8 +2393,8 @@ extern int ext4_check_all_de(struct inode *dir, struct buffer_head *bh,
23762393
extern int ext4_sync_file(struct file *, loff_t, loff_t, int);
23772394

23782395
/* hash.c */
2379-
extern int ext4fs_dirhash(const char *name, int len, struct
2380-
dx_hash_info *hinfo);
2396+
extern int ext4fs_dirhash(const struct inode *dir, const char *name, int len,
2397+
struct dx_hash_info *hinfo);
23812398

23822399
/* ialloc.c */
23832400
extern struct inode *__ext4_new_inode(handle_t *, struct inode *, umode_t,
@@ -2973,6 +2990,10 @@ static inline void ext4_unlock_group(struct super_block *sb,
29732990
/* dir.c */
29742991
extern const struct file_operations ext4_dir_operations;
29752992

2993+
#ifdef CONFIG_UNICODE
2994+
extern const struct dentry_operations ext4_dentry_ops;
2995+
#endif
2996+
29762997
/* file.c */
29772998
extern const struct inode_operations ext4_file_inode_operations;
29782999
extern const struct file_operations ext4_file_operations;
@@ -3065,6 +3086,10 @@ extern void initialize_dirent_tail(struct ext4_dir_entry_tail *t,
30653086
extern int ext4_handle_dirty_dirent_node(handle_t *handle,
30663087
struct inode *inode,
30673088
struct buffer_head *bh);
3089+
extern int ext4_ci_compare(const struct inode *parent,
3090+
const struct qstr *name,
3091+
const struct qstr *entry);
3092+
30683093
#define S_SHIFT 12
30693094
static const unsigned char ext4_type_by_mode[(S_IFMT >> S_SHIFT) + 1] = {
30703095
[S_IFREG >> S_SHIFT] = EXT4_FT_REG_FILE,

fs/ext4/extents_status.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -711,7 +711,7 @@ static void ext4_es_insert_extent_ind_check(struct inode *inode,
711711
* We don't need to check unwritten extent because
712712
* indirect-based file doesn't have it.
713713
*/
714-
BUG_ON(1);
714+
BUG();
715715
}
716716
} else if (retval == 0) {
717717
if (ext4_es_is_written(es)) {
@@ -780,7 +780,7 @@ static int __es_insert_extent(struct inode *inode, struct extent_status *newes)
780780
}
781781
p = &(*p)->rb_right;
782782
} else {
783-
BUG_ON(1);
783+
BUG();
784784
return -EINVAL;
785785
}
786786
}

0 commit comments

Comments
 (0)