2018-07-18 21:44:41 +08:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2017 Red Hat, Inc.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License version 2 as published by
|
|
|
|
* the Free Software Foundation.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/cred.h>
|
|
|
|
#include <linux/file.h>
|
|
|
|
#include <linux/xattr.h>
|
|
|
|
#include "overlayfs.h"
|
|
|
|
|
|
|
|
static struct file *ovl_open_realfile(const struct file *file)
|
|
|
|
{
|
|
|
|
struct inode *inode = file_inode(file);
|
|
|
|
struct inode *upperinode = ovl_inode_upper(inode);
|
|
|
|
struct inode *realinode = upperinode ?: ovl_inode_lower(inode);
|
|
|
|
struct file *realfile;
|
|
|
|
const struct cred *old_cred;
|
|
|
|
|
|
|
|
old_cred = ovl_override_creds(inode->i_sb);
|
|
|
|
realfile = open_with_fake_path(&file->f_path, file->f_flags | O_NOATIME,
|
|
|
|
realinode, current_cred());
|
|
|
|
revert_creds(old_cred);
|
|
|
|
|
|
|
|
pr_debug("open(%p[%pD2/%c], 0%o) -> (%p, 0%o)\n",
|
|
|
|
file, file, upperinode ? 'u' : 'l', file->f_flags,
|
|
|
|
realfile, IS_ERR(realfile) ? 0 : realfile->f_flags);
|
|
|
|
|
|
|
|
return realfile;
|
|
|
|
}
|
|
|
|
|
2018-07-18 21:44:41 +08:00
|
|
|
#define OVL_SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT)
|
|
|
|
|
|
|
|
static int ovl_change_flags(struct file *file, unsigned int flags)
|
|
|
|
{
|
|
|
|
struct inode *inode = file_inode(file);
|
|
|
|
int err;
|
|
|
|
|
|
|
|
/* No atime modificaton on underlying */
|
|
|
|
flags |= O_NOATIME;
|
|
|
|
|
|
|
|
/* If some flag changed that cannot be changed then something's amiss */
|
|
|
|
if (WARN_ON((file->f_flags ^ flags) & ~OVL_SETFL_MASK))
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
flags &= OVL_SETFL_MASK;
|
|
|
|
|
|
|
|
if (((flags ^ file->f_flags) & O_APPEND) && IS_APPEND(inode))
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
if (flags & O_DIRECT) {
|
|
|
|
if (!file->f_mapping->a_ops ||
|
|
|
|
!file->f_mapping->a_ops->direct_IO)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (file->f_op->check_flags) {
|
|
|
|
err = file->f_op->check_flags(flags);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock(&file->f_lock);
|
|
|
|
file->f_flags = (file->f_flags & ~OVL_SETFL_MASK) | flags;
|
|
|
|
spin_unlock(&file->f_lock);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ovl_real_fdget(const struct file *file, struct fd *real)
|
|
|
|
{
|
|
|
|
struct inode *inode = file_inode(file);
|
|
|
|
|
|
|
|
real->flags = 0;
|
|
|
|
real->file = file->private_data;
|
|
|
|
|
|
|
|
/* Has it been copied up since we'd opened it? */
|
|
|
|
if (unlikely(file_inode(real->file) != ovl_inode_real(inode))) {
|
|
|
|
real->flags = FDPUT_FPUT;
|
|
|
|
real->file = ovl_open_realfile(file);
|
|
|
|
|
|
|
|
return PTR_ERR_OR_ZERO(real->file);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Did the flags change since open? */
|
|
|
|
if (unlikely((file->f_flags ^ real->file->f_flags) & ~O_NOATIME))
|
|
|
|
return ovl_change_flags(real->file, file->f_flags);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-07-18 21:44:41 +08:00
|
|
|
static int ovl_open(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
struct dentry *dentry = file_dentry(file);
|
|
|
|
struct file *realfile;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = ovl_open_maybe_copy_up(dentry, file->f_flags);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
/* No longer need these flags, so don't pass them on to underlying fs */
|
|
|
|
file->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
|
|
|
|
|
|
|
|
realfile = ovl_open_realfile(file);
|
|
|
|
if (IS_ERR(realfile))
|
|
|
|
return PTR_ERR(realfile);
|
|
|
|
|
|
|
|
file->private_data = realfile;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ovl_release(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
fput(file->private_data);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
|
|
|
|
{
|
|
|
|
struct inode *realinode = ovl_inode_real(file_inode(file));
|
|
|
|
|
|
|
|
return generic_file_llseek_size(file, offset, whence,
|
|
|
|
realinode->i_sb->s_maxbytes,
|
|
|
|
i_size_read(realinode));
|
|
|
|
}
|
|
|
|
|
|
|
|
const struct file_operations ovl_file_operations = {
|
|
|
|
.open = ovl_open,
|
|
|
|
.release = ovl_release,
|
|
|
|
.llseek = ovl_llseek,
|
|
|
|
};
|