vfs-6.13.tmpfs

-----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCZzcZIgAKCRCRxhvAZXjc
 oge4AQDxhsKW+v/jKHydzqzwG3Ks7DIxrUg/mcGfdtBwjiWgvwEA8t0QAAfKECAK
 B0+bNKJ8XJRUtZ10Jgm3dzURbEhBWgU=
 =4Lui
 -----END PGP SIGNATURE-----

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()
This commit is contained in:
Linus Torvalds 2024-11-18 11:05:26 -08:00
commit 7956186e75
9 changed files with 371 additions and 23 deletions

View File

@ -241,6 +241,28 @@ So 'mount -t tmpfs -o size=10G,nr_inodes=10k,mode=700 tmpfs /mytmpfs'
will give you tmpfs instance on /mytmpfs which can allocate 10GB
RAM/SWAP in 10240 inodes and it is only accessible by root.
tmpfs has the following mounting options for case-insensitive lookup support:
================= ==============================================================
casefold Enable casefold support at this mount point using the given
argument as the encoding standard. Currently only UTF-8
encodings are supported. If no argument is used, it will load
the latest UTF-8 encoding available.
strict_encoding Enable strict encoding at this mount point (disabled by
default). In this mode, the filesystem refuses to create file
and directory with names containing invalid UTF-8 characters.
================= ==============================================================
This option doesn't render the entire filesystem case-insensitive. One needs to
still set the casefold flag per directory, by flipping the +F attribute in an
empty directory. Nevertheless, new directories will inherit the attribute. The
mountpoint itself cannot be made case-insensitive.
Example::
$ mount -t tmpfs -o casefold=utf8-12.1.0,strict_encoding fs_name /mytmpfs
$ mount -t tmpfs -o casefold fs_name /mytmpfs
:Author:
Christoph Rohland <cr@sap.com>, 1.12.01
@ -250,3 +272,5 @@ RAM/SWAP in 10240 inodes and it is only accessible by root.
KOSAKI Motohiro, 16 Mar 2010
:Updated:
Chris Down, 13 July 2020
:Updated:
André Almeida, 23 Aug 2024

View File

@ -2395,11 +2395,8 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
if (fscrypt_is_nokey_name(dentry))
return -ENOKEY;
#if IS_ENABLED(CONFIG_UNICODE)
if (sb_has_strict_encoding(sb) && IS_CASEFOLDED(dir) &&
utf8_validate(sb->s_encoding, &dentry->d_name))
if (!generic_ci_validate_strict_name(dir, &dentry->d_name))
return -EINVAL;
#endif
retval = ext4_fname_setup_filename(dir, &dentry->d_name, 0, &fname);
if (retval)

View File

@ -77,6 +77,10 @@ struct dentry *simple_lookup(struct inode *dir, struct dentry *dentry, unsigned
return ERR_PTR(-ENAMETOOLONG);
if (!dentry->d_sb->s_d_op)
d_set_d_op(dentry, &simple_dentry_operations);
if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir))
return NULL;
d_add(dentry, NULL);
return NULL;
}
@ -1791,7 +1795,7 @@ bool is_empty_dir_inode(struct inode *inode)
*
* Return: 0 if names match, 1 if mismatch, or -ERRNO
*/
static int generic_ci_d_compare(const struct dentry *dentry, unsigned int len,
int generic_ci_d_compare(const struct dentry *dentry, unsigned int len,
const char *str, const struct qstr *name)
{
const struct dentry *parent;
@ -1835,6 +1839,7 @@ static int generic_ci_d_compare(const struct dentry *dentry, unsigned int len,
return utf8_strncasecmp(dentry->d_sb->s_encoding, name, &qstr);
}
EXPORT_SYMBOL(generic_ci_d_compare);
/**
* 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,
*
* Return: 0 if hash was successful or unchanged, and -EINVAL on error
*/
static int generic_ci_d_hash(const struct dentry *dentry, struct qstr *str)
int generic_ci_d_hash(const struct dentry *dentry, struct qstr *str)
{
const struct inode *dir = READ_ONCE(dentry->d_inode);
struct super_block *sb = dentry->d_sb;
@ -1858,6 +1863,7 @@ static int generic_ci_d_hash(const struct dentry *dentry, struct qstr *str)
return -EINVAL;
return 0;
}
EXPORT_SYMBOL(generic_ci_d_hash);
static const struct dentry_operations generic_ci_dentry_ops = {
.d_hash = generic_ci_d_hash,

View File

@ -214,3 +214,29 @@ void utf8_unload(struct unicode_map *um)
}
EXPORT_SYMBOL(utf8_unload);
/**
* utf8_parse_version - Parse a UTF-8 version number from a string
*
* @version: input string
*
* Returns the parsed version on success, negative code on error
*/
int utf8_parse_version(char *version)
{
substring_t args[3];
unsigned int maj, min, rev;
static const struct match_token token[] = {
{1, "%d.%d.%d"},
{0, NULL}
};
if (match_token(version, token, args) != 1)
return -EINVAL;
if (match_int(&args[0], &maj) || match_int(&args[1], &min) ||
match_int(&args[2], &rev))
return -EINVAL;
return UNICODE_AGE(maj, min, rev);
}
EXPORT_SYMBOL(utf8_parse_version);

View File

@ -17,9 +17,6 @@
static unsigned int failed_tests;
static unsigned int total_tests;
/* Tests will be based on this version. */
#define UTF8_LATEST UNICODE_AGE(12, 1, 0)
#define _test(cond, func, line, fmt, ...) do { \
total_tests++; \
if (!cond) { \

View File

@ -46,6 +46,7 @@
#include <linux/maple_tree.h>
#include <linux/rw_hint.h>
#include <linux/file_ref.h>
#include <linux/unicode.h>
#include <asm/byteorder.h>
#include <uapi/linux/fs.h>
@ -3479,6 +3480,54 @@ extern int generic_ci_match(const struct inode *parent,
const struct qstr *folded_name,
const u8 *de_name, u32 de_name_len);
#if IS_ENABLED(CONFIG_UNICODE)
int generic_ci_d_hash(const struct dentry *dentry, struct qstr *str);
int generic_ci_d_compare(const struct dentry *dentry, unsigned int len,
const char *str, const struct qstr *name);
/**
* generic_ci_validate_strict_name - Check if a given name is suitable
* for a directory
*
* This functions checks if the proposed filename is valid for the
* parent directory. That means that only valid UTF-8 filenames will be
* accepted for casefold directories from filesystems created with the
* strict encoding flag. That also means that any name will be
* accepted for directories that doesn't have casefold enabled, or
* aren't being strict with the encoding.
*
* @dir: inode of the directory where the new file will be created
* @name: name of the new file
*
* Return:
* * True: if the filename is suitable for this directory. It can be
* true if a given name is not suitable for a strict encoding
* directory, but the directory being used isn't strict
* * False if the filename isn't suitable for this directory. This only
* happens when a directory is casefolded and the filesystem is strict
* about its encoding.
*/
static inline bool generic_ci_validate_strict_name(struct inode *dir, struct qstr *name)
{
if (!IS_CASEFOLDED(dir) || !sb_has_strict_encoding(dir->i_sb))
return true;
/*
* A casefold dir must have a encoding set, unless the filesystem
* is corrupted
*/
if (WARN_ON_ONCE(!dir->i_sb->s_encoding))
return true;
return !utf8_validate(dir->i_sb->s_encoding, name);
}
#else
static inline bool generic_ci_validate_strict_name(struct inode *dir, struct qstr *name)
{
return true;
}
#endif
static inline bool sb_has_encoding(const struct super_block *sb)
{
#if IS_ENABLED(CONFIG_UNICODE)

View File

@ -42,10 +42,10 @@ struct shmem_inode_info {
struct inode vfs_inode;
};
#define SHMEM_FL_USER_VISIBLE FS_FL_USER_VISIBLE
#define SHMEM_FL_USER_VISIBLE (FS_FL_USER_VISIBLE | FS_CASEFOLD_FL)
#define SHMEM_FL_USER_MODIFIABLE \
(FS_IMMUTABLE_FL | FS_APPEND_FL | FS_NODUMP_FL | FS_NOATIME_FL)
#define SHMEM_FL_INHERITED (FS_NODUMP_FL | FS_NOATIME_FL)
(FS_IMMUTABLE_FL | FS_APPEND_FL | FS_NODUMP_FL | FS_NOATIME_FL | FS_CASEFOLD_FL)
#define SHMEM_FL_INHERITED (FS_NODUMP_FL | FS_NOATIME_FL | FS_CASEFOLD_FL)
struct shmem_quota_limits {
qsize_t usrquota_bhardlimit; /* Default user quota block hard limit */

View File

@ -16,6 +16,8 @@ struct utf8data_table;
((unsigned int)(MIN) << UNICODE_MIN_SHIFT) | \
((unsigned int)(REV)))
#define UTF8_LATEST UNICODE_AGE(12, 1, 0)
static inline u8 unicode_major(unsigned int age)
{
return (age >> UNICODE_MAJ_SHIFT) & 0xff;
@ -76,4 +78,6 @@ int utf8_casefold_hash(const struct unicode_map *um, const void *salt,
struct unicode_map *utf8_load(unsigned int version);
void utf8_unload(struct unicode_map *um);
int utf8_parse_version(char *version);
#endif /* _LINUX_UNICODE_H */

View File

@ -40,6 +40,7 @@
#include <linux/fs_parser.h>
#include <linux/swapfile.h>
#include <linux/iversion.h>
#include <linux/unicode.h>
#include "swap.h"
static struct vfsmount *shm_mnt __ro_after_init;
@ -123,6 +124,10 @@ struct shmem_options {
bool noswap;
unsigned short quota_types;
struct shmem_quota_limits qlimits;
#if IS_ENABLED(CONFIG_UNICODE)
struct unicode_map *encoding;
bool strict_encoding;
#endif
#define SHMEM_SEEN_BLOCKS 1
#define SHMEM_SEEN_INODES 2
#define SHMEM_SEEN_HUGE 4
@ -2749,13 +2754,62 @@ static int shmem_file_open(struct inode *inode, struct file *file)
#ifdef CONFIG_TMPFS_XATTR
static int shmem_initxattrs(struct inode *, const struct xattr *, void *);
#if IS_ENABLED(CONFIG_UNICODE)
/*
* shmem_inode_casefold_flags - Deal with casefold file attribute flag
*
* The casefold file attribute needs some special checks. I can just be added to
* an empty dir, and can't be removed from a non-empty dir.
*/
static int shmem_inode_casefold_flags(struct inode *inode, unsigned int fsflags,
struct dentry *dentry, unsigned int *i_flags)
{
unsigned int old = inode->i_flags;
struct super_block *sb = inode->i_sb;
if (fsflags & FS_CASEFOLD_FL) {
if (!(old & S_CASEFOLD)) {
if (!sb->s_encoding)
return -EOPNOTSUPP;
if (!S_ISDIR(inode->i_mode))
return -ENOTDIR;
if (dentry && !simple_empty(dentry))
return -ENOTEMPTY;
}
*i_flags = *i_flags | S_CASEFOLD;
} else if (old & S_CASEFOLD) {
if (dentry && !simple_empty(dentry))
return -ENOTEMPTY;
}
return 0;
}
#else
static int shmem_inode_casefold_flags(struct inode *inode, unsigned int fsflags,
struct dentry *dentry, unsigned int *i_flags)
{
if (fsflags & FS_CASEFOLD_FL)
return -EOPNOTSUPP;
return 0;
}
#endif
/*
* chattr's fsflags are unrelated to extended attributes,
* but tmpfs has chosen to enable them under the same config option.
*/
static void shmem_set_inode_flags(struct inode *inode, unsigned int fsflags)
static int shmem_set_inode_flags(struct inode *inode, unsigned int fsflags, struct dentry *dentry)
{
unsigned int i_flags = 0;
int ret;
ret = shmem_inode_casefold_flags(inode, fsflags, dentry, &i_flags);
if (ret)
return ret;
if (fsflags & FS_NOATIME_FL)
i_flags |= S_NOATIME;
@ -2766,10 +2820,12 @@ static void shmem_set_inode_flags(struct inode *inode, unsigned int fsflags)
/*
* But FS_NODUMP_FL does not require any action in i_flags.
*/
inode_set_flags(inode, i_flags, S_NOATIME | S_APPEND | S_IMMUTABLE);
inode_set_flags(inode, i_flags, S_NOATIME | S_APPEND | S_IMMUTABLE | S_CASEFOLD);
return 0;
}
#else
static void shmem_set_inode_flags(struct inode *inode, unsigned int fsflags)
static void shmem_set_inode_flags(struct inode *inode, unsigned int fsflags, struct dentry *dentry)
{
}
#define shmem_initxattrs NULL
@ -2816,7 +2872,7 @@ static struct inode *__shmem_get_inode(struct mnt_idmap *idmap,
info->fsflags = (dir == NULL) ? 0 :
SHMEM_I(dir)->fsflags & SHMEM_FL_INHERITED;
if (info->fsflags)
shmem_set_inode_flags(inode, info->fsflags);
shmem_set_inode_flags(inode, info->fsflags, NULL);
INIT_LIST_HEAD(&info->shrinklist);
INIT_LIST_HEAD(&info->swaplist);
simple_xattrs_init(&info->xattrs);
@ -3562,6 +3618,9 @@ shmem_mknod(struct mnt_idmap *idmap, struct inode *dir,
struct inode *inode;
int error;
if (!generic_ci_validate_strict_name(dir, &dentry->d_name))
return -EINVAL;
inode = shmem_get_inode(idmap, dir->i_sb, dir, mode, dev, VM_NORESERVE);
if (IS_ERR(inode))
return PTR_ERR(inode);
@ -3581,7 +3640,12 @@ shmem_mknod(struct mnt_idmap *idmap, struct inode *dir,
dir->i_size += BOGO_DIRENT_SIZE;
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
inode_inc_iversion(dir);
if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir))
d_add(dentry, inode);
else
d_instantiate(dentry, inode);
dget(dentry); /* Extra count - pin the dentry in core */
return error;
@ -3672,6 +3736,9 @@ static int shmem_link(struct dentry *old_dentry, struct inode *dir,
inc_nlink(inode);
ihold(inode); /* New dentry reference */
dget(dentry); /* Extra pinning count for the created dentry */
if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir))
d_add(dentry, inode);
else
d_instantiate(dentry, inode);
out:
return ret;
@ -3692,6 +3759,14 @@ static int shmem_unlink(struct inode *dir, struct dentry *dentry)
inode_inc_iversion(dir);
drop_nlink(inode);
dput(dentry); /* Undo the count from "create" - does all the work */
/*
* For now, VFS can't deal with case-insensitive negative dentries, so
* we invalidate them
*/
if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir))
d_invalidate(dentry);
return 0;
}
@ -3836,6 +3911,9 @@ static int shmem_symlink(struct mnt_idmap *idmap, struct inode *dir,
dir->i_size += BOGO_DIRENT_SIZE;
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
inode_inc_iversion(dir);
if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir))
d_add(dentry, inode);
else
d_instantiate(dentry, inode);
dget(dentry);
return 0;
@ -3901,16 +3979,23 @@ static int shmem_fileattr_set(struct mnt_idmap *idmap,
{
struct inode *inode = d_inode(dentry);
struct shmem_inode_info *info = SHMEM_I(inode);
int ret, flags;
if (fileattr_has_fsx(fa))
return -EOPNOTSUPP;
if (fa->flags & ~SHMEM_FL_USER_MODIFIABLE)
return -EOPNOTSUPP;
info->fsflags = (info->fsflags & ~SHMEM_FL_USER_MODIFIABLE) |
flags = (info->fsflags & ~SHMEM_FL_USER_MODIFIABLE) |
(fa->flags & SHMEM_FL_USER_MODIFIABLE);
shmem_set_inode_flags(inode, info->fsflags);
ret = shmem_set_inode_flags(inode, flags, dentry);
if (ret)
return ret;
info->fsflags = flags;
inode_set_ctime_current(inode);
inode_inc_iversion(inode);
return 0;
@ -4189,6 +4274,9 @@ enum shmem_param {
Opt_usrquota_inode_hardlimit,
Opt_grpquota_block_hardlimit,
Opt_grpquota_inode_hardlimit,
Opt_casefold_version,
Opt_casefold,
Opt_strict_encoding,
};
static const struct constant_table shmem_param_enums_huge[] = {
@ -4220,9 +4308,54 @@ const struct fs_parameter_spec shmem_fs_parameters[] = {
fsparam_string("grpquota_block_hardlimit", Opt_grpquota_block_hardlimit),
fsparam_string("grpquota_inode_hardlimit", Opt_grpquota_inode_hardlimit),
#endif
fsparam_string("casefold", Opt_casefold_version),
fsparam_flag ("casefold", Opt_casefold),
fsparam_flag ("strict_encoding", Opt_strict_encoding),
{}
};
#if IS_ENABLED(CONFIG_UNICODE)
static int shmem_parse_opt_casefold(struct fs_context *fc, struct fs_parameter *param,
bool latest_version)
{
struct shmem_options *ctx = fc->fs_private;
unsigned int version = UTF8_LATEST;
struct unicode_map *encoding;
char *version_str = param->string + 5;
if (!latest_version) {
if (strncmp(param->string, "utf8-", 5))
return invalfc(fc, "Only UTF-8 encodings are supported "
"in the format: utf8-<version number>");
version = utf8_parse_version(version_str);
if (version < 0)
return invalfc(fc, "Invalid UTF-8 version: %s", version_str);
}
encoding = utf8_load(version);
if (IS_ERR(encoding)) {
return invalfc(fc, "Failed loading UTF-8 version: utf8-%u.%u.%u\n",
unicode_major(version), unicode_minor(version),
unicode_rev(version));
}
pr_info("tmpfs: Using encoding : utf8-%u.%u.%u\n",
unicode_major(version), unicode_minor(version), unicode_rev(version));
ctx->encoding = encoding;
return 0;
}
#else
static int shmem_parse_opt_casefold(struct fs_context *fc, struct fs_parameter *param,
bool latest_version)
{
return invalfc(fc, "tmpfs: Kernel not built with CONFIG_UNICODE\n");
}
#endif
static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param)
{
struct shmem_options *ctx = fc->fs_private;
@ -4381,6 +4514,17 @@ static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param)
"Group quota inode hardlimit too large.");
ctx->qlimits.grpquota_ihardlimit = size;
break;
case Opt_casefold_version:
return shmem_parse_opt_casefold(fc, param, false);
case Opt_casefold:
return shmem_parse_opt_casefold(fc, param, true);
case Opt_strict_encoding:
#if IS_ENABLED(CONFIG_UNICODE)
ctx->strict_encoding = true;
break;
#else
return invalfc(fc, "tmpfs: Kernel not built with CONFIG_UNICODE\n");
#endif
}
return 0;
@ -4610,6 +4754,11 @@ static void shmem_put_super(struct super_block *sb)
{
struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
#if IS_ENABLED(CONFIG_UNICODE)
if (sb->s_encoding)
utf8_unload(sb->s_encoding);
#endif
#ifdef CONFIG_TMPFS_QUOTA
shmem_disable_quotas(sb);
#endif
@ -4620,6 +4769,14 @@ static void shmem_put_super(struct super_block *sb)
sb->s_fs_info = NULL;
}
#if IS_ENABLED(CONFIG_UNICODE) && defined(CONFIG_TMPFS)
static const struct dentry_operations shmem_ci_dentry_ops = {
.d_hash = generic_ci_d_hash,
.d_compare = generic_ci_d_compare,
.d_delete = always_delete_dentry,
};
#endif
static int shmem_fill_super(struct super_block *sb, struct fs_context *fc)
{
struct shmem_options *ctx = fc->fs_private;
@ -4654,9 +4811,25 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc)
}
sb->s_export_op = &shmem_export_ops;
sb->s_flags |= SB_NOSEC | SB_I_VERSION;
#if IS_ENABLED(CONFIG_UNICODE)
if (!ctx->encoding && ctx->strict_encoding) {
pr_err("tmpfs: strict_encoding option without encoding is forbidden\n");
error = -EINVAL;
goto failed;
}
if (ctx->encoding) {
sb->s_encoding = ctx->encoding;
sb->s_d_op = &shmem_ci_dentry_ops;
if (ctx->strict_encoding)
sb->s_encoding_flags = SB_ENC_STRICT_MODE_FL;
}
#endif
#else
sb->s_flags |= SB_NOUSER;
#endif
#endif /* CONFIG_TMPFS */
sbinfo->max_blocks = ctx->blocks;
sbinfo->max_inodes = ctx->inodes;
sbinfo->free_ispace = sbinfo->max_inodes * BOGO_INODE_SIZE;
@ -4930,6 +5103,10 @@ int shmem_init_fs_context(struct fs_context *fc)
ctx->uid = current_fsuid();
ctx->gid = current_fsgid();
#if IS_ENABLED(CONFIG_UNICODE)
ctx->encoding = NULL;
#endif
fc->fs_private = ctx;
fc->ops = &shmem_fs_context_ops;
return 0;
@ -4946,6 +5123,66 @@ static struct file_system_type shmem_fs_type = {
.fs_flags = FS_USERNS_MOUNT | FS_ALLOW_IDMAP | FS_MGTIME,
};
#if defined(CONFIG_SYSFS) && defined(CONFIG_TMPFS)
#define __INIT_KOBJ_ATTR(_name, _mode, _show, _store) \
{ \
.attr = { .name = __stringify(_name), .mode = _mode }, \
.show = _show, \
.store = _store, \
}
#define TMPFS_ATTR_W(_name, _store) \
static struct kobj_attribute tmpfs_attr_##_name = \
__INIT_KOBJ_ATTR(_name, 0200, NULL, _store)
#define TMPFS_ATTR_RW(_name, _show, _store) \
static struct kobj_attribute tmpfs_attr_##_name = \
__INIT_KOBJ_ATTR(_name, 0644, _show, _store)
#define TMPFS_ATTR_RO(_name, _show) \
static struct kobj_attribute tmpfs_attr_##_name = \
__INIT_KOBJ_ATTR(_name, 0444, _show, NULL)
#if IS_ENABLED(CONFIG_UNICODE)
static ssize_t casefold_show(struct kobject *kobj, struct kobj_attribute *a,
char *buf)
{
return sysfs_emit(buf, "supported\n");
}
TMPFS_ATTR_RO(casefold, casefold_show);
#endif
static struct attribute *tmpfs_attributes[] = {
#if IS_ENABLED(CONFIG_UNICODE)
&tmpfs_attr_casefold.attr,
#endif
NULL
};
static const struct attribute_group tmpfs_attribute_group = {
.attrs = tmpfs_attributes,
.name = "features"
};
static struct kobject *tmpfs_kobj;
static int __init tmpfs_sysfs_init(void)
{
int ret;
tmpfs_kobj = kobject_create_and_add("tmpfs", fs_kobj);
if (!tmpfs_kobj)
return -ENOMEM;
ret = sysfs_create_group(tmpfs_kobj, &tmpfs_attribute_group);
if (ret)
kobject_put(tmpfs_kobj);
return ret;
}
#endif /* CONFIG_SYSFS && CONFIG_TMPFS */
void __init shmem_init(void)
{
int error;
@ -4969,6 +5206,14 @@ void __init shmem_init(void)
goto out1;
}
#if defined(CONFIG_SYSFS) && defined(CONFIG_TMPFS)
error = tmpfs_sysfs_init();
if (error) {
pr_err("Could not init tmpfs sysfs\n");
goto out1;
}
#endif
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
if (has_transparent_hugepage() && shmem_huge > SHMEM_HUGE_DENY)
SHMEM_SB(shm_mnt->mnt_sb)->huge = shmem_huge;