overlayfs update for 5.2
-----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQSQHSd0lITzzeNWNm3h3BK/laaZPAUCXNpu6gAKCRDh3BK/laaZ PNYnAQCLMJBZp9AVKU+5onOGKLmgUfnbKZhWJYICW6DVKobo6AEA4aXBIk5TIDiu +a3Ny0nAutdpHcRkbi8jJty91BeJgQg= =iLSD -----END PGP SIGNATURE----- Merge tag 'ovl-update-5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs Pull overlayfs update from Miklos Szeredi: "Just bug fixes in this small update" * tag 'ovl-update-5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs: ovl: relax WARN_ON() for overlapping layers use case ovl: check the capability before cred overridden ovl: do not generate duplicate fsnotify events for "fake" path ovl: support stacked SEEK_HOLE/SEEK_DATA ovl: fix missing upper fs freeze protection on copy up for ioctl
This commit is contained in:
commit
7e9890a350
|
@ -909,14 +909,14 @@ static bool ovl_open_need_copy_up(struct dentry *dentry, int flags)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags)
|
int ovl_maybe_copy_up(struct dentry *dentry, int flags)
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
if (ovl_open_need_copy_up(dentry, file_flags)) {
|
if (ovl_open_need_copy_up(dentry, flags)) {
|
||||||
err = ovl_want_write(dentry);
|
err = ovl_want_write(dentry);
|
||||||
if (!err) {
|
if (!err) {
|
||||||
err = ovl_copy_up_flags(dentry, file_flags);
|
err = ovl_copy_up_flags(dentry, flags);
|
||||||
ovl_drop_write(dentry);
|
ovl_drop_write(dentry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -260,7 +260,7 @@ static int ovl_instantiate(struct dentry *dentry, struct inode *inode,
|
||||||
* hashed directory inode aliases.
|
* hashed directory inode aliases.
|
||||||
*/
|
*/
|
||||||
inode = ovl_get_inode(dentry->d_sb, &oip);
|
inode = ovl_get_inode(dentry->d_sb, &oip);
|
||||||
if (WARN_ON(IS_ERR(inode)))
|
if (IS_ERR(inode))
|
||||||
return PTR_ERR(inode);
|
return PTR_ERR(inode);
|
||||||
} else {
|
} else {
|
||||||
WARN_ON(ovl_inode_real(inode) != d_inode(newdentry));
|
WARN_ON(ovl_inode_real(inode) != d_inode(newdentry));
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <linux/mount.h>
|
#include <linux/mount.h>
|
||||||
#include <linux/xattr.h>
|
#include <linux/xattr.h>
|
||||||
#include <linux/uio.h>
|
#include <linux/uio.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
#include "overlayfs.h"
|
#include "overlayfs.h"
|
||||||
|
|
||||||
static char ovl_whatisit(struct inode *inode, struct inode *realinode)
|
static char ovl_whatisit(struct inode *inode, struct inode *realinode)
|
||||||
|
@ -29,10 +30,11 @@ static struct file *ovl_open_realfile(const struct file *file,
|
||||||
struct inode *inode = file_inode(file);
|
struct inode *inode = file_inode(file);
|
||||||
struct file *realfile;
|
struct file *realfile;
|
||||||
const struct cred *old_cred;
|
const struct cred *old_cred;
|
||||||
|
int flags = file->f_flags | O_NOATIME | FMODE_NONOTIFY;
|
||||||
|
|
||||||
old_cred = ovl_override_creds(inode->i_sb);
|
old_cred = ovl_override_creds(inode->i_sb);
|
||||||
realfile = open_with_fake_path(&file->f_path, file->f_flags | O_NOATIME,
|
realfile = open_with_fake_path(&file->f_path, flags, realinode,
|
||||||
realinode, current_cred());
|
current_cred());
|
||||||
revert_creds(old_cred);
|
revert_creds(old_cred);
|
||||||
|
|
||||||
pr_debug("open(%p[%pD2/%c], 0%o) -> (%p, 0%o)\n",
|
pr_debug("open(%p[%pD2/%c], 0%o) -> (%p, 0%o)\n",
|
||||||
|
@ -50,7 +52,7 @@ static int ovl_change_flags(struct file *file, unsigned int flags)
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
/* No atime modificaton on underlying */
|
/* No atime modificaton on underlying */
|
||||||
flags |= O_NOATIME;
|
flags |= O_NOATIME | FMODE_NONOTIFY;
|
||||||
|
|
||||||
/* If some flag changed that cannot be changed then something's amiss */
|
/* If some flag changed that cannot be changed then something's amiss */
|
||||||
if (WARN_ON((file->f_flags ^ flags) & ~OVL_SETFL_MASK))
|
if (WARN_ON((file->f_flags ^ flags) & ~OVL_SETFL_MASK))
|
||||||
|
@ -116,11 +118,10 @@ static int ovl_real_fdget(const struct file *file, struct fd *real)
|
||||||
|
|
||||||
static int ovl_open(struct inode *inode, struct file *file)
|
static int ovl_open(struct inode *inode, struct file *file)
|
||||||
{
|
{
|
||||||
struct dentry *dentry = file_dentry(file);
|
|
||||||
struct file *realfile;
|
struct file *realfile;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = ovl_open_maybe_copy_up(dentry, file->f_flags);
|
err = ovl_maybe_copy_up(file_dentry(file), file->f_flags);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
@ -145,11 +146,47 @@ static int ovl_release(struct inode *inode, struct file *file)
|
||||||
|
|
||||||
static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
|
static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
|
||||||
{
|
{
|
||||||
struct inode *realinode = ovl_inode_real(file_inode(file));
|
struct inode *inode = file_inode(file);
|
||||||
|
struct fd real;
|
||||||
|
const struct cred *old_cred;
|
||||||
|
ssize_t ret;
|
||||||
|
|
||||||
return generic_file_llseek_size(file, offset, whence,
|
/*
|
||||||
realinode->i_sb->s_maxbytes,
|
* The two special cases below do not need to involve real fs,
|
||||||
i_size_read(realinode));
|
* so we can optimizing concurrent callers.
|
||||||
|
*/
|
||||||
|
if (offset == 0) {
|
||||||
|
if (whence == SEEK_CUR)
|
||||||
|
return file->f_pos;
|
||||||
|
|
||||||
|
if (whence == SEEK_SET)
|
||||||
|
return vfs_setpos(file, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ovl_real_fdget(file, &real);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Overlay file f_pos is the master copy that is preserved
|
||||||
|
* through copy up and modified on read/write, but only real
|
||||||
|
* fs knows how to SEEK_HOLE/SEEK_DATA and real fs may impose
|
||||||
|
* limitations that are more strict than ->s_maxbytes for specific
|
||||||
|
* files, so we use the real file to perform seeks.
|
||||||
|
*/
|
||||||
|
inode_lock(inode);
|
||||||
|
real.file->f_pos = file->f_pos;
|
||||||
|
|
||||||
|
old_cred = ovl_override_creds(inode->i_sb);
|
||||||
|
ret = vfs_llseek(real.file, offset, whence);
|
||||||
|
revert_creds(old_cred);
|
||||||
|
|
||||||
|
file->f_pos = real.file->f_pos;
|
||||||
|
inode_unlock(inode);
|
||||||
|
|
||||||
|
fdput(real);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ovl_file_accessed(struct file *file)
|
static void ovl_file_accessed(struct file *file)
|
||||||
|
@ -372,10 +409,68 @@ static long ovl_real_ioctl(struct file *file, unsigned int cmd,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
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 long arg)
|
||||||
{
|
{
|
||||||
long ret;
|
long ret;
|
||||||
struct inode *inode = file_inode(file);
|
struct inode *inode = file_inode(file);
|
||||||
|
unsigned int flags;
|
||||||
|
unsigned int old_flags;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
inode_lock(inode);
|
||||||
|
|
||||||
|
/* 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)) &&
|
||||||
|
!capable(CAP_LINUX_IMMUTABLE))
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
ret = ovl_maybe_copy_up(file_dentry(file), O_WRONLY);
|
||||||
|
if (ret)
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
ret = ovl_real_ioctl(file, FS_IOC_SETFLAGS, arg);
|
||||||
|
|
||||||
|
ovl_copyflags(ovl_inode_real(inode), inode);
|
||||||
|
unlock:
|
||||||
|
inode_unlock(inode);
|
||||||
|
|
||||||
|
mnt_drop_write_file(file);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||||
|
{
|
||||||
|
long ret;
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case FS_IOC_GETFLAGS:
|
case FS_IOC_GETFLAGS:
|
||||||
|
@ -383,23 +478,7 @@ static long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FS_IOC_SETFLAGS:
|
case FS_IOC_SETFLAGS:
|
||||||
if (!inode_owner_or_capable(inode))
|
ret = ovl_ioctl_set_flags(file, arg);
|
||||||
return -EACCES;
|
|
||||||
|
|
||||||
ret = mnt_want_write_file(file);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = ovl_copy_up_with_data(file_dentry(file));
|
|
||||||
if (!ret) {
|
|
||||||
ret = ovl_real_ioctl(file, cmd, arg);
|
|
||||||
|
|
||||||
inode_lock(inode);
|
|
||||||
ovl_copyflags(ovl_inode_real(inode), inode);
|
|
||||||
inode_unlock(inode);
|
|
||||||
}
|
|
||||||
|
|
||||||
mnt_drop_write_file(file);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -832,7 +832,7 @@ struct inode *ovl_get_inode(struct super_block *sb,
|
||||||
int fsid = bylower ? oip->lowerpath->layer->fsid : 0;
|
int fsid = bylower ? oip->lowerpath->layer->fsid : 0;
|
||||||
bool is_dir, metacopy = false;
|
bool is_dir, metacopy = false;
|
||||||
unsigned long ino = 0;
|
unsigned long ino = 0;
|
||||||
int err = -ENOMEM;
|
int err = oip->newinode ? -EEXIST : -ENOMEM;
|
||||||
|
|
||||||
if (!realinode)
|
if (!realinode)
|
||||||
realinode = d_inode(lowerdentry);
|
realinode = d_inode(lowerdentry);
|
||||||
|
@ -917,6 +917,7 @@ struct inode *ovl_get_inode(struct super_block *sb,
|
||||||
return inode;
|
return inode;
|
||||||
|
|
||||||
out_err:
|
out_err:
|
||||||
|
pr_warn_ratelimited("overlayfs: failed to get inode (%i)\n", err);
|
||||||
inode = ERR_PTR(err);
|
inode = ERR_PTR(err);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
|
@ -421,7 +421,7 @@ extern const struct file_operations ovl_file_operations;
|
||||||
int ovl_copy_up(struct dentry *dentry);
|
int ovl_copy_up(struct dentry *dentry);
|
||||||
int ovl_copy_up_with_data(struct dentry *dentry);
|
int ovl_copy_up_with_data(struct dentry *dentry);
|
||||||
int ovl_copy_up_flags(struct dentry *dentry, int flags);
|
int ovl_copy_up_flags(struct dentry *dentry, int flags);
|
||||||
int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags);
|
int ovl_maybe_copy_up(struct dentry *dentry, int flags);
|
||||||
int ovl_copy_xattr(struct dentry *old, struct dentry *new);
|
int ovl_copy_xattr(struct dentry *old, struct dentry *new);
|
||||||
int ovl_set_attr(struct dentry *upper, struct kstat *stat);
|
int ovl_set_attr(struct dentry *upper, struct kstat *stat);
|
||||||
struct ovl_fh *ovl_encode_real_fh(struct dentry *real, bool is_upper);
|
struct ovl_fh *ovl_encode_real_fh(struct dentry *real, bool is_upper);
|
||||||
|
|
Loading…
Reference in New Issue