overlayfs fixes for 5.2-rc6

-----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQSQHSd0lITzzeNWNm3h3BK/laaZPAUCXQvlwwAKCRDh3BK/laaZ
 PJH4AP4vnumu1Q22ZWiGkTeU93JTgHt3MGPG1r1DtnUmsIKRfwEAjDY8bvuOP7Vw
 EYQicghvAPTHWqyGUoe0QZJwPlMiZw4=
 =TKOM
 -----END PGP SIGNATURE-----

Merge tag 'ovl-fixes-5.2-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs

Pull overlayfs fixes from Miklos Szeredi:
 "Fix two regressions in this cycle, and a couple of older bugs"

* tag 'ovl-fixes-5.2-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs:
  ovl: make i_ino consistent with st_ino in more cases
  ovl: fix typo in MODULE_PARM_DESC
  ovl: fix bogus -Wmaybe-unitialized warning
  ovl: don't fail with disconnected lower NFS
  ovl: fix wrong flags check in FS_IOC_FS[SG]ETXATTR ioctls
This commit is contained in:
Linus Torvalds 2019-06-20 14:19:34 -07:00
commit 4ae004a9bc
5 changed files with 90 additions and 59 deletions

View File

@ -37,7 +37,7 @@ static int ovl_ccup_get(char *buf, const struct kernel_param *param)
}
module_param_call(check_copy_up, ovl_ccup_set, ovl_ccup_get, NULL, 0644);
MODULE_PARM_DESC(ovl_check_copy_up, "Obsolete; does nothing");
MODULE_PARM_DESC(check_copy_up, "Obsolete; does nothing");
int ovl_copy_xattr(struct dentry *old, struct dentry *new)
{

View File

@ -21,7 +21,7 @@
static unsigned short ovl_redirect_max = 256;
module_param_named(redirect_max, ovl_redirect_max, ushort, 0644);
MODULE_PARM_DESC(ovl_redirect_max,
MODULE_PARM_DESC(redirect_max,
"Maximum length of absolute redirect xattr value");
static int ovl_set_redirect(struct dentry *dentry, bool samedir);

View File

@ -409,37 +409,16 @@ static long ovl_real_ioctl(struct file *file, unsigned int cmd,
return ret;
}
static unsigned int ovl_get_inode_flags(struct inode *inode)
{
unsigned int flags = READ_ONCE(inode->i_flags);
unsigned int ovl_iflags = 0;
if (flags & S_SYNC)
ovl_iflags |= FS_SYNC_FL;
if (flags & S_APPEND)
ovl_iflags |= FS_APPEND_FL;
if (flags & S_IMMUTABLE)
ovl_iflags |= FS_IMMUTABLE_FL;
if (flags & S_NOATIME)
ovl_iflags |= FS_NOATIME_FL;
return ovl_iflags;
}
static long ovl_ioctl_set_flags(struct file *file, unsigned int cmd,
unsigned long arg)
unsigned long arg, unsigned int iflags)
{
long ret;
struct inode *inode = file_inode(file);
unsigned int flags;
unsigned int old_flags;
unsigned int old_iflags;
if (!inode_owner_or_capable(inode))
return -EACCES;
if (get_user(flags, (int __user *) arg))
return -EFAULT;
ret = mnt_want_write_file(file);
if (ret)
return ret;
@ -448,8 +427,8 @@ static long ovl_ioctl_set_flags(struct file *file, unsigned int cmd,
/* Check the capability before cred override */
ret = -EPERM;
old_flags = ovl_get_inode_flags(inode);
if (((flags ^ old_flags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) &&
old_iflags = READ_ONCE(inode->i_flags);
if (((iflags ^ old_iflags) & (S_APPEND | S_IMMUTABLE)) &&
!capable(CAP_LINUX_IMMUTABLE))
goto unlock;
@ -469,6 +448,63 @@ unlock:
}
static unsigned int ovl_fsflags_to_iflags(unsigned int flags)
{
unsigned int iflags = 0;
if (flags & FS_SYNC_FL)
iflags |= S_SYNC;
if (flags & FS_APPEND_FL)
iflags |= S_APPEND;
if (flags & FS_IMMUTABLE_FL)
iflags |= S_IMMUTABLE;
if (flags & FS_NOATIME_FL)
iflags |= S_NOATIME;
return iflags;
}
static long ovl_ioctl_set_fsflags(struct file *file, unsigned int cmd,
unsigned long arg)
{
unsigned int flags;
if (get_user(flags, (int __user *) arg))
return -EFAULT;
return ovl_ioctl_set_flags(file, cmd, arg,
ovl_fsflags_to_iflags(flags));
}
static unsigned int ovl_fsxflags_to_iflags(unsigned int xflags)
{
unsigned int iflags = 0;
if (xflags & FS_XFLAG_SYNC)
iflags |= S_SYNC;
if (xflags & FS_XFLAG_APPEND)
iflags |= S_APPEND;
if (xflags & FS_XFLAG_IMMUTABLE)
iflags |= S_IMMUTABLE;
if (xflags & FS_XFLAG_NOATIME)
iflags |= S_NOATIME;
return iflags;
}
static long ovl_ioctl_set_fsxflags(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct fsxattr fa;
memset(&fa, 0, sizeof(fa));
if (copy_from_user(&fa, (void __user *) arg, sizeof(fa)))
return -EFAULT;
return ovl_ioctl_set_flags(file, cmd, arg,
ovl_fsxflags_to_iflags(fa.fsx_xflags));
}
static long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
long ret;
@ -480,8 +516,11 @@ static long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
break;
case FS_IOC_SETFLAGS:
ret = ovl_ioctl_set_fsflags(file, cmd, arg);
break;
case FS_IOC_FSSETXATTR:
ret = ovl_ioctl_set_flags(file, cmd, arg);
ret = ovl_ioctl_set_fsxflags(file, cmd, arg);
break;
default:

View File

@ -553,15 +553,15 @@ static void ovl_fill_inode(struct inode *inode, umode_t mode, dev_t rdev,
int xinobits = ovl_xino_bits(inode->i_sb);
/*
* When NFS export is enabled and d_ino is consistent with st_ino
* (samefs or i_ino has enough bits to encode layer), set the same
* value used for d_ino to i_ino, because nfsd readdirplus compares
* d_ino values to i_ino values of child entries. When called from
* When d_ino is consistent with st_ino (samefs or i_ino has enough
* bits to encode layer), set the same value used for st_ino to i_ino,
* so inode number exposed via /proc/locks and a like will be
* consistent with d_ino and st_ino values. An i_ino value inconsistent
* with d_ino also causes nfsd readdirplus to fail. When called from
* ovl_new_inode(), ino arg is 0, so i_ino will be updated to real
* upper inode i_ino on ovl_inode_init() or ovl_inode_update().
*/
if (inode->i_sb->s_export_op &&
(ovl_same_sb(inode->i_sb) || xinobits)) {
if (ovl_same_sb(inode->i_sb) || xinobits) {
inode->i_ino = ino;
if (xinobits && fsid && !(ino >> (64 - xinobits)))
inode->i_ino |= (unsigned long)fsid << (64 - xinobits);

View File

@ -31,29 +31,29 @@ struct ovl_dir_cache;
static bool ovl_redirect_dir_def = IS_ENABLED(CONFIG_OVERLAY_FS_REDIRECT_DIR);
module_param_named(redirect_dir, ovl_redirect_dir_def, bool, 0644);
MODULE_PARM_DESC(ovl_redirect_dir_def,
MODULE_PARM_DESC(redirect_dir,
"Default to on or off for the redirect_dir feature");
static bool ovl_redirect_always_follow =
IS_ENABLED(CONFIG_OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW);
module_param_named(redirect_always_follow, ovl_redirect_always_follow,
bool, 0644);
MODULE_PARM_DESC(ovl_redirect_always_follow,
MODULE_PARM_DESC(redirect_always_follow,
"Follow redirects even if redirect_dir feature is turned off");
static bool ovl_index_def = IS_ENABLED(CONFIG_OVERLAY_FS_INDEX);
module_param_named(index, ovl_index_def, bool, 0644);
MODULE_PARM_DESC(ovl_index_def,
MODULE_PARM_DESC(index,
"Default to on or off for the inodes index feature");
static bool ovl_nfs_export_def = IS_ENABLED(CONFIG_OVERLAY_FS_NFS_EXPORT);
module_param_named(nfs_export, ovl_nfs_export_def, bool, 0644);
MODULE_PARM_DESC(ovl_nfs_export_def,
MODULE_PARM_DESC(nfs_export,
"Default to on or off for the NFS export feature");
static bool ovl_xino_auto_def = IS_ENABLED(CONFIG_OVERLAY_FS_XINO_AUTO);
module_param_named(xino_auto, ovl_xino_auto_def, bool, 0644);
MODULE_PARM_DESC(ovl_xino_auto_def,
MODULE_PARM_DESC(xino_auto,
"Auto enable xino feature");
static void ovl_entry_stack_free(struct ovl_entry *oe)
@ -66,7 +66,7 @@ static void ovl_entry_stack_free(struct ovl_entry *oe)
static bool ovl_metacopy_def = IS_ENABLED(CONFIG_OVERLAY_FS_METACOPY);
module_param_named(metacopy, ovl_metacopy_def, bool, 0644);
MODULE_PARM_DESC(ovl_metacopy_def,
MODULE_PARM_DESC(metacopy,
"Default to on or off for the metadata only copy up feature");
static void ovl_dentry_release(struct dentry *dentry)
@ -995,8 +995,8 @@ static int ovl_setup_trap(struct super_block *sb, struct dentry *dir,
int err;
trap = ovl_get_trap_inode(sb, dir);
err = PTR_ERR(trap);
if (IS_ERR(trap)) {
err = PTR_ERR_OR_ZERO(trap);
if (err) {
if (err == -ELOOP)
pr_err("overlayfs: conflicting %s path\n", name);
return err;
@ -1471,23 +1471,20 @@ out_err:
* Check if this layer root is a descendant of:
* - another layer of this overlayfs instance
* - upper/work dir of any overlayfs instance
* - a disconnected dentry (detached root)
*/
static int ovl_check_layer(struct super_block *sb, struct dentry *dentry,
const char *name)
{
struct dentry *next, *parent;
bool is_root = false;
struct dentry *next = dentry, *parent;
int err = 0;
if (!dentry || dentry == dentry->d_sb->s_root)
if (!dentry)
return 0;
next = dget(dentry);
/* Walk back ancestors to fs root (inclusive) looking for traps */
do {
parent = dget_parent(next);
is_root = (parent == next);
parent = dget_parent(next);
/* Walk back ancestors to root (inclusive) looking for traps */
while (!err && parent != next) {
if (ovl_is_inuse(parent)) {
err = -EBUSY;
pr_err("overlayfs: %s path overlapping in-use upperdir/workdir\n",
@ -1496,17 +1493,12 @@ static int ovl_check_layer(struct super_block *sb, struct dentry *dentry,
err = -ELOOP;
pr_err("overlayfs: overlapping %s path\n", name);
}
dput(next);
next = parent;
} while (!err && !is_root);
/* Did we really walk to fs root or found a detached root? */
if (!err && next != dentry->d_sb->s_root) {
err = -ESTALE;
pr_err("overlayfs: disconnected %s path\n", name);
parent = dget_parent(next);
dput(next);
}
dput(next);
dput(parent);
return err;
}