mm/shmem: fix chattr fsflags support in tmpfs

ext[234] have always allowed unimplemented chattr flags to be set, but
other filesystems have tended to be stricter.  Follow the stricter
approach for tmpfs: I don't want to have to explain why csu attributes
don't actually work, and we won't need to update the chattr(1) manpage;
and it's never wrong to start off strict, relaxing later if persuaded. 
Allow only a (append only) i (immutable) A (no atime) and d (no dump).

Although lsattr showed 'A' inherited, the NOATIME behavior was not being
inherited: because nothing sync'ed FS_NOATIME_FL to S_NOATIME.  Add
shmem_set_inode_flags() to sync the flags, using inode_set_flags() to
avoid that instant of lost immutablility during fileattr_set().

But that change switched generic/079 from passing to failing: because
FS_IMMUTABLE_FL and FS_APPEND_FL had been unconventionally included in the
INHERITED fsflags: remove them and generic/079 is back to passing.

Link: https://lkml.kernel.org/r/2961dcb0-ddf3-b9f0-3268-12a4ff996856@google.com
Fixes: e408e695f5 ("mm/shmem: support FS_IOC_[SG]ETFLAGS in tmpfs")
Signed-off-by: Hugh Dickins <hughd@google.com>
Cc: "Theodore Ts'o" <tytso@mit.edu>
Cc: Radoslaw Burny <rburny@google.com>
Cc: "Darrick J. Wong" <djwong@kernel.org>
Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
Hugh Dickins 2022-08-10 21:51:09 -07:00 committed by Andrew Morton
parent 1d8d14641f
commit cb241339b9
2 changed files with 35 additions and 32 deletions

View File

@ -29,15 +29,10 @@ struct shmem_inode_info {
struct inode vfs_inode; struct inode vfs_inode;
}; };
#define SHMEM_FL_USER_VISIBLE FS_FL_USER_VISIBLE #define SHMEM_FL_USER_VISIBLE FS_FL_USER_VISIBLE
#define SHMEM_FL_USER_MODIFIABLE FS_FL_USER_MODIFIABLE #define SHMEM_FL_USER_MODIFIABLE \
#define SHMEM_FL_INHERITED FS_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)
/* Flags that are appropriate for regular files (all but dir-specific ones). */
#define SHMEM_REG_FLMASK (~(FS_DIRSYNC_FL | FS_TOPDIR_FL))
/* Flags that are appropriate for non-directories/regular files. */
#define SHMEM_OTHER_FLMASK (FS_NODUMP_FL | FS_NOATIME_FL)
struct shmem_sb_info { struct shmem_sb_info {
unsigned long max_blocks; /* How many blocks are allowed */ unsigned long max_blocks; /* How many blocks are allowed */

View File

@ -2281,16 +2281,34 @@ static int shmem_mmap(struct file *file, struct vm_area_struct *vma)
return 0; return 0;
} }
/* Mask out flags that are inappropriate for the given type of inode. */ #ifdef CONFIG_TMPFS_XATTR
static unsigned shmem_mask_flags(umode_t mode, __u32 flags) static int shmem_initxattrs(struct inode *, const struct xattr *, void *);
/*
* 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)
{ {
if (S_ISDIR(mode)) unsigned int i_flags = 0;
return flags;
else if (S_ISREG(mode)) if (fsflags & FS_NOATIME_FL)
return flags & SHMEM_REG_FLMASK; i_flags |= S_NOATIME;
else if (fsflags & FS_APPEND_FL)
return flags & SHMEM_OTHER_FLMASK; i_flags |= S_APPEND;
if (fsflags & FS_IMMUTABLE_FL)
i_flags |= S_IMMUTABLE;
/*
* But FS_NODUMP_FL does not require any action in i_flags.
*/
inode_set_flags(inode, i_flags, S_NOATIME | S_APPEND | S_IMMUTABLE);
} }
#else
static void shmem_set_inode_flags(struct inode *inode, unsigned int fsflags)
{
}
#define shmem_initxattrs NULL
#endif
static struct inode *shmem_get_inode(struct super_block *sb, struct inode *dir, static struct inode *shmem_get_inode(struct super_block *sb, struct inode *dir,
umode_t mode, dev_t dev, unsigned long flags) umode_t mode, dev_t dev, unsigned long flags)
@ -2319,7 +2337,8 @@ static struct inode *shmem_get_inode(struct super_block *sb, struct inode *dir,
info->i_crtime = inode->i_mtime; info->i_crtime = inode->i_mtime;
info->fsflags = (dir == NULL) ? 0 : info->fsflags = (dir == NULL) ? 0 :
SHMEM_I(dir)->fsflags & SHMEM_FL_INHERITED; SHMEM_I(dir)->fsflags & SHMEM_FL_INHERITED;
info->fsflags = shmem_mask_flags(mode, info->fsflags); if (info->fsflags)
shmem_set_inode_flags(inode, info->fsflags);
INIT_LIST_HEAD(&info->shrinklist); INIT_LIST_HEAD(&info->shrinklist);
INIT_LIST_HEAD(&info->swaplist); INIT_LIST_HEAD(&info->swaplist);
simple_xattrs_init(&info->xattrs); simple_xattrs_init(&info->xattrs);
@ -2468,12 +2487,6 @@ out_unacct_blocks:
static const struct inode_operations shmem_symlink_inode_operations; static const struct inode_operations shmem_symlink_inode_operations;
static const struct inode_operations shmem_short_symlink_operations; static const struct inode_operations shmem_short_symlink_operations;
#ifdef CONFIG_TMPFS_XATTR
static int shmem_initxattrs(struct inode *, const struct xattr *, void *);
#else
#define shmem_initxattrs NULL
#endif
static int static int
shmem_write_begin(struct file *file, struct address_space *mapping, shmem_write_begin(struct file *file, struct address_space *mapping,
loff_t pos, unsigned len, loff_t pos, unsigned len,
@ -3179,18 +3192,13 @@ static int shmem_fileattr_set(struct user_namespace *mnt_userns,
if (fileattr_has_fsx(fa)) if (fileattr_has_fsx(fa))
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (fa->flags & ~SHMEM_FL_USER_MODIFIABLE)
return -EOPNOTSUPP;
info->fsflags = (info->fsflags & ~SHMEM_FL_USER_MODIFIABLE) | info->fsflags = (info->fsflags & ~SHMEM_FL_USER_MODIFIABLE) |
(fa->flags & SHMEM_FL_USER_MODIFIABLE); (fa->flags & SHMEM_FL_USER_MODIFIABLE);
inode->i_flags &= ~(S_APPEND | S_IMMUTABLE | S_NOATIME); shmem_set_inode_flags(inode, info->fsflags);
if (info->fsflags & FS_APPEND_FL)
inode->i_flags |= S_APPEND;
if (info->fsflags & FS_IMMUTABLE_FL)
inode->i_flags |= S_IMMUTABLE;
if (info->fsflags & FS_NOATIME_FL)
inode->i_flags |= S_NOATIME;
inode->i_ctime = current_time(inode); inode->i_ctime = current_time(inode);
return 0; return 0;
} }