Skip to content

Commit 7956186

Browse files
committed
Merge tag 'vfs-6.13.tmpfs' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
Pull tmpfs case folding updates from Christian Brauner: "This adds case-insensitive support for tmpfs. The work contained in here adds support for case-insensitive file names lookups in tmpfs. The main difference from other casefold filesystems is that tmpfs has no information on disk, just on RAM, so we can't use mkfs to create a case-insensitive tmpfs. For this implementation, there's a mount option for casefolding. The rest of the patchset follows a similar approach as ext4 and f2fs. The use case for this feature is similar to the use case for ext4, to better support compatibility layers (like Wine), particularly in combination with sandboxing/container tools (like Flatpak). Those containerization tools can share a subset of the host filesystem with an application. In the container, the root directory and any parent directories required for a shared directory are on tmpfs, with the shared directories bind-mounted into the container's view of the filesystem. If the host filesystem is using case-insensitive directories, then the application can do lookups inside those directories in a case-insensitive way, without this needing to be implemented in user-space. However, if the host is only sharing a subset of a case-insensitive directory with the application, then the parent directories of the mount point will be part of the container's root tmpfs. When the application tries to do case-insensitive lookups of those parent directories on a case-sensitive tmpfs, the lookup will fail" * tag 'vfs-6.13.tmpfs' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: tmpfs: Initialize sysfs during tmpfs init tmpfs: Fix type for sysfs' casefold attribute libfs: Fix kernel-doc warning in generic_ci_validate_strict_name docs: tmpfs: Add casefold options tmpfs: Expose filesystem features via sysfs tmpfs: Add flag FS_CASEFOLD_FL support for tmpfs dirs tmpfs: Add casefold lookup support libfs: Export generic_ci_ dentry functions unicode: Recreate utf8_parse_version() unicode: Export latest available UTF-8 version number ext4: Use generic_ci_validate_strict_name helper libfs: Create the helper function generic_ci_validate_strict_name()
2 parents a5ca574 + 552b151 commit 7956186

File tree

9 files changed

+371
-23
lines changed

9 files changed

+371
-23
lines changed

Documentation/filesystems/tmpfs.rst

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,28 @@ So 'mount -t tmpfs -o size=10G,nr_inodes=10k,mode=700 tmpfs /mytmpfs'
241241
will give you tmpfs instance on /mytmpfs which can allocate 10GB
242242
RAM/SWAP in 10240 inodes and it is only accessible by root.
243243

244+
tmpfs has the following mounting options for case-insensitive lookup support:
245+
246+
================= ==============================================================
247+
casefold Enable casefold support at this mount point using the given
248+
argument as the encoding standard. Currently only UTF-8
249+
encodings are supported. If no argument is used, it will load
250+
the latest UTF-8 encoding available.
251+
strict_encoding Enable strict encoding at this mount point (disabled by
252+
default). In this mode, the filesystem refuses to create file
253+
and directory with names containing invalid UTF-8 characters.
254+
================= ==============================================================
255+
256+
This option doesn't render the entire filesystem case-insensitive. One needs to
257+
still set the casefold flag per directory, by flipping the +F attribute in an
258+
empty directory. Nevertheless, new directories will inherit the attribute. The
259+
mountpoint itself cannot be made case-insensitive.
260+
261+
Example::
262+
263+
$ mount -t tmpfs -o casefold=utf8-12.1.0,strict_encoding fs_name /mytmpfs
264+
$ mount -t tmpfs -o casefold fs_name /mytmpfs
265+
244266

245267
:Author:
246268
Christoph Rohland <[email protected]>, 1.12.01
@@ -250,3 +272,5 @@ RAM/SWAP in 10240 inodes and it is only accessible by root.
250272
KOSAKI Motohiro, 16 Mar 2010
251273
:Updated:
252274
Chris Down, 13 July 2020
275+
:Updated:
276+
André Almeida, 23 Aug 2024

fs/ext4/namei.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2395,11 +2395,8 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
23952395
if (fscrypt_is_nokey_name(dentry))
23962396
return -ENOKEY;
23972397

2398-
#if IS_ENABLED(CONFIG_UNICODE)
2399-
if (sb_has_strict_encoding(sb) && IS_CASEFOLDED(dir) &&
2400-
utf8_validate(sb->s_encoding, &dentry->d_name))
2398+
if (!generic_ci_validate_strict_name(dir, &dentry->d_name))
24012399
return -EINVAL;
2402-
#endif
24032400

24042401
retval = ext4_fname_setup_filename(dir, &dentry->d_name, 0, &fname);
24052402
if (retval)

fs/libfs.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ struct dentry *simple_lookup(struct inode *dir, struct dentry *dentry, unsigned
7777
return ERR_PTR(-ENAMETOOLONG);
7878
if (!dentry->d_sb->s_d_op)
7979
d_set_d_op(dentry, &simple_dentry_operations);
80+
81+
if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir))
82+
return NULL;
83+
8084
d_add(dentry, NULL);
8185
return NULL;
8286
}
@@ -1791,8 +1795,8 @@ bool is_empty_dir_inode(struct inode *inode)
17911795
*
17921796
* Return: 0 if names match, 1 if mismatch, or -ERRNO
17931797
*/
1794-
static int generic_ci_d_compare(const struct dentry *dentry, unsigned int len,
1795-
const char *str, const struct qstr *name)
1798+
int generic_ci_d_compare(const struct dentry *dentry, unsigned int len,
1799+
const char *str, const struct qstr *name)
17961800
{
17971801
const struct dentry *parent;
17981802
const struct inode *dir;
@@ -1835,6 +1839,7 @@ static int generic_ci_d_compare(const struct dentry *dentry, unsigned int len,
18351839

18361840
return utf8_strncasecmp(dentry->d_sb->s_encoding, name, &qstr);
18371841
}
1842+
EXPORT_SYMBOL(generic_ci_d_compare);
18381843

18391844
/**
18401845
* generic_ci_d_hash - generic d_hash implementation for casefolding filesystems
@@ -1843,7 +1848,7 @@ static int generic_ci_d_compare(const struct dentry *dentry, unsigned int len,
18431848
*
18441849
* Return: 0 if hash was successful or unchanged, and -EINVAL on error
18451850
*/
1846-
static int generic_ci_d_hash(const struct dentry *dentry, struct qstr *str)
1851+
int generic_ci_d_hash(const struct dentry *dentry, struct qstr *str)
18471852
{
18481853
const struct inode *dir = READ_ONCE(dentry->d_inode);
18491854
struct super_block *sb = dentry->d_sb;
@@ -1858,6 +1863,7 @@ static int generic_ci_d_hash(const struct dentry *dentry, struct qstr *str)
18581863
return -EINVAL;
18591864
return 0;
18601865
}
1866+
EXPORT_SYMBOL(generic_ci_d_hash);
18611867

18621868
static const struct dentry_operations generic_ci_dentry_ops = {
18631869
.d_hash = generic_ci_d_hash,

fs/unicode/utf8-core.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,3 +214,29 @@ void utf8_unload(struct unicode_map *um)
214214
}
215215
EXPORT_SYMBOL(utf8_unload);
216216

217+
/**
218+
* utf8_parse_version - Parse a UTF-8 version number from a string
219+
*
220+
* @version: input string
221+
*
222+
* Returns the parsed version on success, negative code on error
223+
*/
224+
int utf8_parse_version(char *version)
225+
{
226+
substring_t args[3];
227+
unsigned int maj, min, rev;
228+
static const struct match_token token[] = {
229+
{1, "%d.%d.%d"},
230+
{0, NULL}
231+
};
232+
233+
if (match_token(version, token, args) != 1)
234+
return -EINVAL;
235+
236+
if (match_int(&args[0], &maj) || match_int(&args[1], &min) ||
237+
match_int(&args[2], &rev))
238+
return -EINVAL;
239+
240+
return UNICODE_AGE(maj, min, rev);
241+
}
242+
EXPORT_SYMBOL(utf8_parse_version);

fs/unicode/utf8-selftest.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@
1717
static unsigned int failed_tests;
1818
static unsigned int total_tests;
1919

20-
/* Tests will be based on this version. */
21-
#define UTF8_LATEST UNICODE_AGE(12, 1, 0)
22-
2320
#define _test(cond, func, line, fmt, ...) do { \
2421
total_tests++; \
2522
if (!cond) { \

include/linux/fs.h

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include <linux/maple_tree.h>
4747
#include <linux/rw_hint.h>
4848
#include <linux/file_ref.h>
49+
#include <linux/unicode.h>
4950

5051
#include <asm/byteorder.h>
5152
#include <uapi/linux/fs.h>
@@ -3479,6 +3480,54 @@ extern int generic_ci_match(const struct inode *parent,
34793480
const struct qstr *folded_name,
34803481
const u8 *de_name, u32 de_name_len);
34813482

3483+
#if IS_ENABLED(CONFIG_UNICODE)
3484+
int generic_ci_d_hash(const struct dentry *dentry, struct qstr *str);
3485+
int generic_ci_d_compare(const struct dentry *dentry, unsigned int len,
3486+
const char *str, const struct qstr *name);
3487+
3488+
/**
3489+
* generic_ci_validate_strict_name - Check if a given name is suitable
3490+
* for a directory
3491+
*
3492+
* This functions checks if the proposed filename is valid for the
3493+
* parent directory. That means that only valid UTF-8 filenames will be
3494+
* accepted for casefold directories from filesystems created with the
3495+
* strict encoding flag. That also means that any name will be
3496+
* accepted for directories that doesn't have casefold enabled, or
3497+
* aren't being strict with the encoding.
3498+
*
3499+
* @dir: inode of the directory where the new file will be created
3500+
* @name: name of the new file
3501+
*
3502+
* Return:
3503+
* * True: if the filename is suitable for this directory. It can be
3504+
* true if a given name is not suitable for a strict encoding
3505+
* directory, but the directory being used isn't strict
3506+
* * False if the filename isn't suitable for this directory. This only
3507+
* happens when a directory is casefolded and the filesystem is strict
3508+
* about its encoding.
3509+
*/
3510+
static inline bool generic_ci_validate_strict_name(struct inode *dir, struct qstr *name)
3511+
{
3512+
if (!IS_CASEFOLDED(dir) || !sb_has_strict_encoding(dir->i_sb))
3513+
return true;
3514+
3515+
/*
3516+
* A casefold dir must have a encoding set, unless the filesystem
3517+
* is corrupted
3518+
*/
3519+
if (WARN_ON_ONCE(!dir->i_sb->s_encoding))
3520+
return true;
3521+
3522+
return !utf8_validate(dir->i_sb->s_encoding, name);
3523+
}
3524+
#else
3525+
static inline bool generic_ci_validate_strict_name(struct inode *dir, struct qstr *name)
3526+
{
3527+
return true;
3528+
}
3529+
#endif
3530+
34823531
static inline bool sb_has_encoding(const struct super_block *sb)
34833532
{
34843533
#if IS_ENABLED(CONFIG_UNICODE)

include/linux/shmem_fs.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ struct shmem_inode_info {
4242
struct inode vfs_inode;
4343
};
4444

45-
#define SHMEM_FL_USER_VISIBLE FS_FL_USER_VISIBLE
45+
#define SHMEM_FL_USER_VISIBLE (FS_FL_USER_VISIBLE | FS_CASEFOLD_FL)
4646
#define SHMEM_FL_USER_MODIFIABLE \
47-
(FS_IMMUTABLE_FL | FS_APPEND_FL | FS_NODUMP_FL | FS_NOATIME_FL)
48-
#define SHMEM_FL_INHERITED (FS_NODUMP_FL | FS_NOATIME_FL)
47+
(FS_IMMUTABLE_FL | FS_APPEND_FL | FS_NODUMP_FL | FS_NOATIME_FL | FS_CASEFOLD_FL)
48+
#define SHMEM_FL_INHERITED (FS_NODUMP_FL | FS_NOATIME_FL | FS_CASEFOLD_FL)
4949

5050
struct shmem_quota_limits {
5151
qsize_t usrquota_bhardlimit; /* Default user quota block hard limit */

include/linux/unicode.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ struct utf8data_table;
1616
((unsigned int)(MIN) << UNICODE_MIN_SHIFT) | \
1717
((unsigned int)(REV)))
1818

19+
#define UTF8_LATEST UNICODE_AGE(12, 1, 0)
20+
1921
static inline u8 unicode_major(unsigned int age)
2022
{
2123
return (age >> UNICODE_MAJ_SHIFT) & 0xff;
@@ -76,4 +78,6 @@ int utf8_casefold_hash(const struct unicode_map *um, const void *salt,
7678
struct unicode_map *utf8_load(unsigned int version);
7779
void utf8_unload(struct unicode_map *um);
7880

81+
int utf8_parse_version(char *version);
82+
7983
#endif /* _LINUX_UNICODE_H */

0 commit comments

Comments
 (0)