Skip to content

Commit 6009fdf

Browse files
author
Ian Kent
committed
tmpfs,xattr: enable limited user extended attributes
JIRA: https://issues.redhat.com/browse/RHEL-65718 Upstream status: Linus Conflicts: In mm/shmem.c there were 3 rejects: 1) hunk #5 due to missing upstream commit a2e4595 ("shmem: stable directory offsets") which added a call to function simple_offset_add() which is not added. 2) hunk #9 due to existing CentOS Stream commit 79e59ae ("shmem: convert to ctime accessor functions") which required the hunk to be applied manually. 3) hunk #10 due to missing Upstream commit 0c95c02 ("fs: drop unused posix acl handlers") which needed to be applied manually to allow for the differences. Also in mm/shmem.c there was a fuzz 2 in hunk #4 due to a white space difference and fuzz 2 in hunk #6 due to existing CentOS Stream commit 79e59ae ("shmem: convert to ctime accessor functions"). commit 2daf18a Author: Hugh Dickins <[email protected]> Date: Tue Aug 8 21:33:56 2023 -0700 tmpfs,xattr: enable limited user extended attributes Enable "user." extended attributes on tmpfs, limiting them by tracking the space they occupy, and deducting that space from the limited ispace (unless tmpfs mounted with nr_inodes=0 to leave that ispace unlimited). tmpfs inodes and simple xattrs are both unswappable, and have to be in lowmem on a 32-bit highmem kernel: so the ispace limit is appropriate for xattrs, without any need for a further mount option. Add simple_xattr_space() to give approximate but deterministic estimate of the space taken up by each xattr: with simple_xattrs_free() outputting the space freed if required (but kernfs and even some tmpfs usages do not require that, so don't waste time on strlen'ing if not needed). Security and trusted xattrs were already supported: for consistency and simplicity, account them from the same pool; though there's a small risk that a tmpfs with enough space before would now be considered too small. When extended attributes are used, "df -i" does show more IUsed and less IFree than can be explained by the inodes: document that (manpage later). xfstests tests/generic which were not run on tmpfs before but now pass: 020 037 062 070 077 097 103 117 337 377 454 486 523 533 611 618 728 with no new failures. Signed-off-by: Hugh Dickins <[email protected]> Reviewed-by: Jan Kara <[email protected]> Reviewed-by: Carlos Maiolino <[email protected]> Message-Id: <[email protected]> Signed-off-by: Christian Brauner <[email protected]> Signed-off-by: Ian Kent <[email protected]>
1 parent eff31cc commit 6009fdf

File tree

6 files changed

+105
-15
lines changed

6 files changed

+105
-15
lines changed

Documentation/filesystems/tmpfs.rst

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ explained further below, some of which can be reconfigured dynamically on the
2121
fly using a remount ('mount -o remount ...') of the filesystem. A tmpfs
2222
filesystem can be resized but it cannot be resized to a size below its current
2323
usage. tmpfs also supports POSIX ACLs, and extended attributes for the
24-
trusted.* and security.* namespaces. ramfs does not use swap and you cannot
25-
modify any parameter for a ramfs filesystem. The size limit of a ramfs
24+
trusted.*, security.* and user.* namespaces. ramfs does not use swap and you
25+
cannot modify any parameter for a ramfs filesystem. The size limit of a ramfs
2626
filesystem is how much memory you have available, and so care must be taken if
2727
used so to not run out of memory.
2828

@@ -97,6 +97,9 @@ mount with such options, since it allows any user with write access to
9797
use up all the memory on the machine; but enhances the scalability of
9898
that instance in a system with many CPUs making intensive use of it.
9999

100+
If nr_inodes is not 0, that limited space for inodes is also used up by
101+
extended attributes: "df -i"'s IUsed and IUse% increase, IFree decreases.
102+
100103
tmpfs blocks may be swapped out, when there is a shortage of memory.
101104
tmpfs has a mount option to disable its use of swap:
102105

fs/Kconfig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,8 @@ config TMPFS_XATTR
209209
Extended attributes are name:value pairs associated with inodes by
210210
the kernel or by users (see the attr(5) manual page for details).
211211

212-
Currently this enables support for the trusted.* and
213-
security.* namespaces.
212+
This enables support for the trusted.*, security.* and user.*
213+
namespaces.
214214

215215
You need this for POSIX ACL support on tmpfs.
216216

fs/kernfs/dir.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -560,7 +560,7 @@ void kernfs_put(struct kernfs_node *kn)
560560
kfree_const(kn->name);
561561

562562
if (kn->iattr) {
563-
simple_xattrs_free(&kn->iattr->xattrs);
563+
simple_xattrs_free(&kn->iattr->xattrs, NULL);
564564
kmem_cache_free(kernfs_iattrs_cache, kn->iattr);
565565
}
566566
spin_lock(&kernfs_idr_lock);

fs/xattr.c

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,26 @@ const char *xattr_full_name(const struct xattr_handler *handler,
10151015
}
10161016
EXPORT_SYMBOL(xattr_full_name);
10171017

1018+
/**
1019+
* simple_xattr_space - estimate the memory used by a simple xattr
1020+
* @name: the full name of the xattr
1021+
* @size: the size of its value
1022+
*
1023+
* This takes no account of how much larger the two slab objects actually are:
1024+
* that would depend on the slab implementation, when what is required is a
1025+
* deterministic number, which grows with name length and size and quantity.
1026+
*
1027+
* Return: The approximate number of bytes of memory used by such an xattr.
1028+
*/
1029+
size_t simple_xattr_space(const char *name, size_t size)
1030+
{
1031+
/*
1032+
* Use "40" instead of sizeof(struct simple_xattr), to return the
1033+
* same result on 32-bit and 64-bit, and even if simple_xattr grows.
1034+
*/
1035+
return 40 + size + strlen(name);
1036+
}
1037+
10181038
/**
10191039
* simple_xattr_free - free an xattr object
10201040
* @xattr: the xattr object
@@ -1339,14 +1359,17 @@ void simple_xattrs_init(struct simple_xattrs *xattrs)
13391359
/**
13401360
* simple_xattrs_free - free xattrs
13411361
* @xattrs: xattr header whose xattrs to destroy
1362+
* @freed_space: approximate number of bytes of memory freed from @xattrs
13421363
*
13431364
* Destroy all xattrs in @xattr. When this is called no one can hold a
13441365
* reference to any of the xattrs anymore.
13451366
*/
1346-
void simple_xattrs_free(struct simple_xattrs *xattrs)
1367+
void simple_xattrs_free(struct simple_xattrs *xattrs, size_t *freed_space)
13471368
{
13481369
struct rb_node *rbp;
13491370

1371+
if (freed_space)
1372+
*freed_space = 0;
13501373
rbp = rb_first(&xattrs->rb_root);
13511374
while (rbp) {
13521375
struct simple_xattr *xattr;
@@ -1355,6 +1378,9 @@ void simple_xattrs_free(struct simple_xattrs *xattrs)
13551378
rbp_next = rb_next(rbp);
13561379
xattr = rb_entry(rbp, struct simple_xattr, rb_node);
13571380
rb_erase(&xattr->rb_node, &xattrs->rb_root);
1381+
if (freed_space)
1382+
*freed_space += simple_xattr_space(xattr->name,
1383+
xattr->size);
13581384
simple_xattr_free(xattr);
13591385
rbp = rbp_next;
13601386
}

include/linux/xattr.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ struct simple_xattr {
114114
};
115115

116116
void simple_xattrs_init(struct simple_xattrs *xattrs);
117-
void simple_xattrs_free(struct simple_xattrs *xattrs);
117+
void simple_xattrs_free(struct simple_xattrs *xattrs, size_t *freed_space);
118+
size_t simple_xattr_space(const char *name, size_t size);
118119
struct simple_xattr *simple_xattr_alloc(const void *value, size_t size);
119120
void simple_xattr_free(struct simple_xattr *xattr);
120121
int simple_xattr_get(struct simple_xattrs *xattrs, const char *name,

mm/shmem.c

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -392,12 +392,12 @@ static int shmem_reserve_inode(struct super_block *sb, ino_t *inop)
392392
return 0;
393393
}
394394

395-
static void shmem_free_inode(struct super_block *sb)
395+
static void shmem_free_inode(struct super_block *sb, size_t freed_ispace)
396396
{
397397
struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
398398
if (sbinfo->max_inodes) {
399399
raw_spin_lock(&sbinfo->stat_lock);
400-
sbinfo->free_ispace += BOGO_INODE_SIZE;
400+
sbinfo->free_ispace += BOGO_INODE_SIZE + freed_ispace;
401401
raw_spin_unlock(&sbinfo->stat_lock);
402402
}
403403
}
@@ -1238,6 +1238,7 @@ static void shmem_evict_inode(struct inode *inode)
12381238
{
12391239
struct shmem_inode_info *info = SHMEM_I(inode);
12401240
struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
1241+
size_t freed = 0;
12411242

12421243
if (shmem_mapping(inode->i_mapping)) {
12431244
shmem_unacct_size(info->flags, inode->i_size);
@@ -1264,9 +1265,9 @@ static void shmem_evict_inode(struct inode *inode)
12641265
}
12651266
}
12661267

1267-
simple_xattrs_free(&info->xattrs);
1268+
simple_xattrs_free(&info->xattrs, sbinfo->max_inodes ? &freed : NULL);
1269+
shmem_free_inode(inode->i_sb, freed);
12681270
WARN_ON(inode->i_blocks);
1269-
shmem_free_inode(inode->i_sb);
12701271
clear_inode(inode);
12711272
#ifdef CONFIG_TMPFS_QUOTA
12721273
dquot_free_inode(inode);
@@ -2471,7 +2472,7 @@ static struct inode *__shmem_get_inode(struct mnt_idmap *idmap,
24712472

24722473
inode = new_inode(sb);
24732474
if (!inode) {
2474-
shmem_free_inode(sb);
2475+
shmem_free_inode(sb, 0);
24752476
return ERR_PTR(-ENOSPC);
24762477
}
24772478

@@ -3345,7 +3346,7 @@ static int shmem_unlink(struct inode *dir, struct dentry *dentry)
33453346
struct inode *inode = d_inode(dentry);
33463347

33473348
if (inode->i_nlink > 1 && !S_ISDIR(inode->i_mode))
3348-
shmem_free_inode(inode->i_sb);
3349+
shmem_free_inode(inode->i_sb, 0);
33493350

33503351
dir->i_size -= BOGO_DIRENT_SIZE;
33513352
dir->i_mtime = inode_set_ctime_to_ts(dir,
@@ -3583,21 +3584,40 @@ static int shmem_initxattrs(struct inode *inode,
35833584
void *fs_info)
35843585
{
35853586
struct shmem_inode_info *info = SHMEM_I(inode);
3587+
struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
35863588
const struct xattr *xattr;
35873589
struct simple_xattr *new_xattr;
3590+
size_t ispace = 0;
35883591
size_t len;
35893592

3593+
if (sbinfo->max_inodes) {
3594+
for (xattr = xattr_array; xattr->name != NULL; xattr++) {
3595+
ispace += simple_xattr_space(xattr->name,
3596+
xattr->value_len + XATTR_SECURITY_PREFIX_LEN);
3597+
}
3598+
if (ispace) {
3599+
raw_spin_lock(&sbinfo->stat_lock);
3600+
if (sbinfo->free_ispace < ispace)
3601+
ispace = 0;
3602+
else
3603+
sbinfo->free_ispace -= ispace;
3604+
raw_spin_unlock(&sbinfo->stat_lock);
3605+
if (!ispace)
3606+
return -ENOSPC;
3607+
}
3608+
}
3609+
35903610
for (xattr = xattr_array; xattr->name != NULL; xattr++) {
35913611
new_xattr = simple_xattr_alloc(xattr->value, xattr->value_len);
35923612
if (!new_xattr)
3593-
return -ENOMEM;
3613+
break;
35943614

35953615
len = strlen(xattr->name) + 1;
35963616
new_xattr->name = kmalloc(XATTR_SECURITY_PREFIX_LEN + len,
35973617
GFP_KERNEL);
35983618
if (!new_xattr->name) {
35993619
kvfree(new_xattr);
3600-
return -ENOMEM;
3620+
break;
36013621
}
36023622

36033623
memcpy(new_xattr->name, XATTR_SECURITY_PREFIX,
@@ -3608,6 +3628,16 @@ static int shmem_initxattrs(struct inode *inode,
36083628
simple_xattr_add(&info->xattrs, new_xattr);
36093629
}
36103630

3631+
if (xattr->name != NULL) {
3632+
if (ispace) {
3633+
raw_spin_lock(&sbinfo->stat_lock);
3634+
sbinfo->free_ispace += ispace;
3635+
raw_spin_unlock(&sbinfo->stat_lock);
3636+
}
3637+
simple_xattrs_free(&info->xattrs, NULL);
3638+
return -ENOMEM;
3639+
}
3640+
36113641
return 0;
36123642
}
36133643

@@ -3628,16 +3658,39 @@ static int shmem_xattr_handler_set(const struct xattr_handler *handler,
36283658
size_t size, int flags)
36293659
{
36303660
struct shmem_inode_info *info = SHMEM_I(inode);
3661+
struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
36313662
struct simple_xattr *old_xattr;
3663+
size_t ispace = 0;
36323664

36333665
name = xattr_full_name(handler, name);
3666+
if (value && sbinfo->max_inodes) {
3667+
ispace = simple_xattr_space(name, size);
3668+
raw_spin_lock(&sbinfo->stat_lock);
3669+
if (sbinfo->free_ispace < ispace)
3670+
ispace = 0;
3671+
else
3672+
sbinfo->free_ispace -= ispace;
3673+
raw_spin_unlock(&sbinfo->stat_lock);
3674+
if (!ispace)
3675+
return -ENOSPC;
3676+
}
3677+
36343678
old_xattr = simple_xattr_set(&info->xattrs, name, value, size, flags);
36353679
if (!IS_ERR(old_xattr)) {
3680+
ispace = 0;
3681+
if (old_xattr && sbinfo->max_inodes)
3682+
ispace = simple_xattr_space(old_xattr->name,
3683+
old_xattr->size);
36363684
simple_xattr_free(old_xattr);
36373685
old_xattr = NULL;
36383686
inode_set_ctime_current(inode);
36393687
inode_inc_iversion(inode);
36403688
}
3689+
if (ispace) {
3690+
raw_spin_lock(&sbinfo->stat_lock);
3691+
sbinfo->free_ispace += ispace;
3692+
raw_spin_unlock(&sbinfo->stat_lock);
3693+
}
36413694
return PTR_ERR(old_xattr);
36423695
}
36433696

@@ -3653,13 +3706,20 @@ static const struct xattr_handler shmem_trusted_xattr_handler = {
36533706
.set = shmem_xattr_handler_set,
36543707
};
36553708

3709+
static const struct xattr_handler shmem_user_xattr_handler = {
3710+
.prefix = XATTR_USER_PREFIX,
3711+
.get = shmem_xattr_handler_get,
3712+
.set = shmem_xattr_handler_set,
3713+
};
3714+
36563715
static const struct xattr_handler *shmem_xattr_handlers[] = {
36573716
#ifdef CONFIG_TMPFS_POSIX_ACL
36583717
&posix_acl_access_xattr_handler,
36593718
&posix_acl_default_xattr_handler,
36603719
#endif
36613720
&shmem_security_xattr_handler,
36623721
&shmem_trusted_xattr_handler,
3722+
&shmem_user_xattr_handler,
36633723
NULL
36643724
};
36653725

0 commit comments

Comments
 (0)