summaryrefslogtreecommitdiff
path: root/fs/overlayfs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2024-11-22 20:55:42 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2024-11-22 20:55:42 -0800
commite7675238b9bf4db0b872d5dbcd53efa31914c98f (patch)
treea01d4a527d3f37b24522002b708451ab29fc078f /fs/overlayfs
parent060fc106b6854d3289d838ac3c98eb17afb261d7 (diff)
parentc8b359dddb418c60df1a69beea01d1b3322bfe83 (diff)
Merge tag 'ovl-update-6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/overlayfs/vfs
Pull overlayfs updates from Amir Goldstein: - Fix a syzbot reported NULL pointer deref with bfs lower layers - Fix a copy up failure of large file from lower fuse fs - Followup cleanup of backing_file API from Miklos - Introduction and use of revert/override_creds_light() helpers, that were suggested by Christian as a mitigation to cache line bouncing and false sharing of fields in overlayfs creator_cred long lived struct cred copy. - Store up to two backing file references (upper and lower) in an ovl_file container instead of storing a single backing file in file->private_data. This is used to avoid the practice of opening a short lived backing file for the duration of some file operations and to avoid the specialized use of FDPUT_FPUT in such occasions, that was getting in the way of Al's fd_file() conversions. * tag 'ovl-update-6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/overlayfs/vfs: ovl: Filter invalid inodes with missing lookup function ovl: convert ovl_real_fdget() callers to ovl_real_file() ovl: convert ovl_real_fdget_path() callers to ovl_real_file_path() ovl: store upper real file in ovl_file struct ovl: allocate a container struct ovl_file for ovl private context ovl: do not open non-data lower file for fsync ovl: Optimize override/revert creds ovl: pass an explicit reference of creators creds to callers ovl: use wrapper ovl_revert_creds() fs/backing-file: Convert to revert/override_creds_light() cred: Add a light version of override/revert_creds() backing-file: clean up the API ovl: properly handle large files in ovl_security_fileattr
Diffstat (limited to 'fs/overlayfs')
-rw-r--r--fs/overlayfs/copy_up.c2
-rw-r--r--fs/overlayfs/dir.c68
-rw-r--r--fs/overlayfs/file.c327
-rw-r--r--fs/overlayfs/inode.c27
-rw-r--r--fs/overlayfs/namei.c10
-rw-r--r--fs/overlayfs/overlayfs.h4
-rw-r--r--fs/overlayfs/readdir.c8
-rw-r--r--fs/overlayfs/util.c14
-rw-r--r--fs/overlayfs/xattrs.c9
9 files changed, 282 insertions, 187 deletions
diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
index ee2cbd044ce6..3601ddfeddc2 100644
--- a/fs/overlayfs/copy_up.c
+++ b/fs/overlayfs/copy_up.c
@@ -1259,7 +1259,7 @@ static int ovl_copy_up_flags(struct dentry *dentry, int flags)
dput(parent);
dput(next);
}
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
return err;
}
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index ab65e98a1def..08e683917d12 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -553,15 +553,17 @@ out_cleanup:
goto out_dput;
}
-static int ovl_setup_cred_for_create(struct dentry *dentry, struct inode *inode,
- umode_t mode, const struct cred *old_cred)
+static const struct cred *ovl_setup_cred_for_create(struct dentry *dentry,
+ struct inode *inode,
+ umode_t mode,
+ const struct cred *old_cred)
{
int err;
struct cred *override_cred;
override_cred = prepare_creds();
if (!override_cred)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
override_cred->fsuid = inode->i_uid;
override_cred->fsgid = inode->i_gid;
@@ -569,19 +571,26 @@ static int ovl_setup_cred_for_create(struct dentry *dentry, struct inode *inode,
old_cred, override_cred);
if (err) {
put_cred(override_cred);
- return err;
+ return ERR_PTR(err);
}
- put_cred(override_creds(override_cred));
- put_cred(override_cred);
- return 0;
+ /*
+ * Caller is going to match this with revert_creds_light() and drop
+ * referenec on the returned creds.
+ * We must be called with creator creds already, otherwise we risk
+ * leaking creds.
+ */
+ old_cred = override_creds_light(override_cred);
+ WARN_ON_ONCE(old_cred != ovl_creds(dentry->d_sb));
+
+ return override_cred;
}
static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
struct ovl_cattr *attr, bool origin)
{
int err;
- const struct cred *old_cred;
+ const struct cred *old_cred, *new_cred = NULL;
struct dentry *parent = dentry->d_parent;
old_cred = ovl_override_creds(dentry->d_sb);
@@ -610,9 +619,13 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
* create a new inode, so just use the ovl mounter's
* fs{u,g}id.
*/
- err = ovl_setup_cred_for_create(dentry, inode, attr->mode, old_cred);
- if (err)
+ new_cred = ovl_setup_cred_for_create(dentry, inode, attr->mode,
+ old_cred);
+ err = PTR_ERR(new_cred);
+ if (IS_ERR(new_cred)) {
+ new_cred = NULL;
goto out_revert_creds;
+ }
}
if (!ovl_dentry_is_whiteout(dentry))
@@ -621,7 +634,8 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
err = ovl_create_over_whiteout(dentry, inode, attr);
out_revert_creds:
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
+ put_cred(new_cred);
return err;
}
@@ -702,7 +716,7 @@ static int ovl_set_link_redirect(struct dentry *dentry)
old_cred = ovl_override_creds(dentry->d_sb);
err = ovl_set_redirect(dentry, false);
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
return err;
}
@@ -912,7 +926,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
err = ovl_remove_upper(dentry, is_dir, &list);
else
err = ovl_remove_and_whiteout(dentry, &list);
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
if (!err) {
if (is_dir)
clear_nlink(dentry->d_inode);
@@ -1292,7 +1306,7 @@ out_dput_old:
out_unlock:
unlock_rename(new_upperdir, old_upperdir);
out_revert_creds:
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
if (update_nlink)
ovl_nlink_end(new);
else
@@ -1306,18 +1320,22 @@ out:
static int ovl_create_tmpfile(struct file *file, struct dentry *dentry,
struct inode *inode, umode_t mode)
{
- const struct cred *old_cred;
+ const struct cred *old_cred, *new_cred = NULL;
struct path realparentpath;
struct file *realfile;
+ struct ovl_file *of;
struct dentry *newdentry;
/* It's okay to set O_NOATIME, since the owner will be current fsuid */
int flags = file->f_flags | OVL_OPEN_FLAGS;
int err;
old_cred = ovl_override_creds(dentry->d_sb);
- err = ovl_setup_cred_for_create(dentry, inode, mode, old_cred);
- if (err)
+ new_cred = ovl_setup_cred_for_create(dentry, inode, mode, old_cred);
+ err = PTR_ERR(new_cred);
+ if (IS_ERR(new_cred)) {
+ new_cred = NULL;
goto out_revert_creds;
+ }
ovl_path_upper(dentry->d_parent, &realparentpath);
realfile = backing_tmpfile_open(&file->f_path, flags, &realparentpath,
@@ -1327,17 +1345,25 @@ static int ovl_create_tmpfile(struct file *file, struct dentry *dentry,
if (err)
goto out_revert_creds;
+ of = ovl_file_alloc(realfile);
+ if (!of) {
+ fput(realfile);
+ err = -ENOMEM;
+ goto out_revert_creds;
+ }
+
/* ovl_instantiate() consumes the newdentry reference on success */
newdentry = dget(realfile->f_path.dentry);
err = ovl_instantiate(dentry, inode, newdentry, false, file);
if (!err) {
- file->private_data = realfile;
+ file->private_data = of;
} else {
dput(newdentry);
- fput(realfile);
+ ovl_file_free(of);
}
out_revert_creds:
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
+ put_cred(new_cred);
return err;
}
@@ -1389,7 +1415,7 @@ static int ovl_tmpfile(struct mnt_idmap *idmap, struct inode *dir,
put_realfile:
/* Without FMODE_OPENED ->release() won't be called on @file */
if (!(file->f_mode & FMODE_OPENED))
- fput(file->private_data);
+ ovl_file_free(file->private_data);
put_inode:
iput(inode);
drop_write:
diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c
index 4444c78e2e0c..969b458100fe 100644
--- a/fs/overlayfs/file.c
+++ b/fs/overlayfs/file.c
@@ -51,7 +51,7 @@ static struct file *ovl_open_realfile(const struct file *file,
realfile = backing_file_open(&file->f_path, flags, realpath,
current_cred());
}
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
pr_debug("open(%p[%pD2/%c], 0%o) -> (%p, 0%o)\n",
file, file, ovl_whatisit(inode, realinode), file->f_flags,
@@ -89,56 +89,110 @@ static int ovl_change_flags(struct file *file, unsigned int flags)
return 0;
}
-static int ovl_real_fdget_meta(const struct file *file, struct fd *real,
- bool allow_meta)
+struct ovl_file {
+ struct file *realfile;
+ struct file *upperfile;
+};
+
+struct ovl_file *ovl_file_alloc(struct file *realfile)
{
- struct dentry *dentry = file_dentry(file);
- struct file *realfile = file->private_data;
- struct path realpath;
- int err;
+ struct ovl_file *of = kzalloc(sizeof(struct ovl_file), GFP_KERNEL);
- real->word = (unsigned long)realfile;
+ if (unlikely(!of))
+ return NULL;
- if (allow_meta) {
- ovl_path_real(dentry, &realpath);
- } else {
- /* lazy lookup and verify of lowerdata */
- err = ovl_verify_lowerdata(dentry);
- if (err)
- return err;
+ of->realfile = realfile;
+ return of;
+}
- ovl_path_realdata(dentry, &realpath);
- }
- if (!realpath.dentry)
- return -EIO;
+void ovl_file_free(struct ovl_file *of)
+{
+ fput(of->realfile);
+ if (of->upperfile)
+ fput(of->upperfile);
+ kfree(of);
+}
- /* Has it been copied up since we'd opened it? */
- if (unlikely(file_inode(realfile) != d_inode(realpath.dentry))) {
- struct file *f = ovl_open_realfile(file, &realpath);
- if (IS_ERR(f))
- return PTR_ERR(f);
- real->word = (unsigned long)f | FDPUT_FPUT;
- return 0;
+static bool ovl_is_real_file(const struct file *realfile,
+ const struct path *realpath)
+{
+ return file_inode(realfile) == d_inode(realpath->dentry);
+}
+
+static struct file *ovl_real_file_path(const struct file *file,
+ struct path *realpath)
+{
+ struct ovl_file *of = file->private_data;
+ struct file *realfile = of->realfile;
+
+ if (WARN_ON_ONCE(!realpath->dentry))
+ return ERR_PTR(-EIO);
+
+ /*
+ * If the realfile that we want is not where the data used to be at
+ * open time, either we'd been copied up, or it's an fsync of a
+ * metacopied file. We need the upperfile either way, so see if it
+ * is already opened and if it is not then open and store it.
+ */
+ if (unlikely(!ovl_is_real_file(realfile, realpath))) {
+ struct file *upperfile = READ_ONCE(of->upperfile);
+ struct file *old;
+
+ if (!upperfile) { /* Nobody opened upperfile yet */
+ upperfile = ovl_open_realfile(file, realpath);
+ if (IS_ERR(upperfile))
+ return upperfile;
+
+ /* Store the upperfile for later */
+ old = cmpxchg_release(&of->upperfile, NULL, upperfile);
+ if (old) { /* Someone opened upperfile before us */
+ fput(upperfile);
+ upperfile = old;
+ }
+ }
+ /*
+ * Stored file must be from the right inode, unless someone's
+ * been corrupting the upper layer.
+ */
+ if (WARN_ON_ONCE(!ovl_is_real_file(upperfile, realpath)))
+ return ERR_PTR(-EIO);
+
+ realfile = upperfile;
}
/* Did the flags change since open? */
- if (unlikely((file->f_flags ^ realfile->f_flags) & ~OVL_OPEN_FLAGS))
- return ovl_change_flags(realfile, file->f_flags);
+ if (unlikely((file->f_flags ^ realfile->f_flags) & ~OVL_OPEN_FLAGS)) {
+ int err = ovl_change_flags(realfile, file->f_flags);
- return 0;
+ if (err)
+ return ERR_PTR(err);
+ }
+
+ return realfile;
}
-static int ovl_real_fdget(const struct file *file, struct fd *real)
+static struct file *ovl_real_file(const struct file *file)
{
- if (d_is_dir(file_dentry(file))) {
+ struct dentry *dentry = file_dentry(file);
+ struct path realpath;
+ int err;
+
+ if (d_is_dir(dentry)) {
struct file *f = ovl_dir_real_file(file, false);
- if (IS_ERR(f))
- return PTR_ERR(f);
- real->word = (unsigned long)f;
- return 0;
+
+ if (WARN_ON_ONCE(!f))
+ return ERR_PTR(-EIO);
+ return f;
}
- return ovl_real_fdget_meta(file, real, false);
+ /* lazy lookup and verify of lowerdata */
+ err = ovl_verify_lowerdata(dentry);
+ if (err)
+ return ERR_PTR(err);
+
+ ovl_path_realdata(dentry, &realpath);
+
+ return ovl_real_file_path(file, &realpath);
}
static int ovl_open(struct inode *inode, struct file *file)
@@ -146,6 +200,7 @@ static int ovl_open(struct inode *inode, struct file *file)
struct dentry *dentry = file_dentry(file);
struct file *realfile;
struct path realpath;
+ struct ovl_file *of;
int err;
/* lazy lookup and verify lowerdata */
@@ -168,22 +223,27 @@ static int ovl_open(struct inode *inode, struct file *file)
if (IS_ERR(realfile))
return PTR_ERR(realfile);
- file->private_data = realfile;
+ of = ovl_file_alloc(realfile);
+ if (!of) {
+ fput(realfile);
+ return -ENOMEM;
+ }
+
+ file->private_data = of;
return 0;
}
static int ovl_release(struct inode *inode, struct file *file)
{
- fput(file->private_data);
-
+ ovl_file_free(file->private_data);
return 0;
}
static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
{
struct inode *inode = file_inode(file);
- struct fd real;
+ struct file *realfile;
const struct cred *old_cred;
loff_t ret;
@@ -199,9 +259,9 @@ static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
return vfs_setpos(file, 0, 0);
}
- ret = ovl_real_fdget(file, &real);
- if (ret)
- return ret;
+ realfile = ovl_real_file(file);
+ if (IS_ERR(realfile))
+ return PTR_ERR(realfile);
/*
* Overlay file f_pos is the master copy that is preserved
@@ -211,17 +271,15 @@ static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
* files, so we use the real file to perform seeks.
*/
ovl_inode_lock(inode);
- fd_file(real)->f_pos = file->f_pos;
+ realfile->f_pos = file->f_pos;
old_cred = ovl_override_creds(inode->i_sb);
- ret = vfs_llseek(fd_file(real), offset, whence);
- revert_creds(old_cred);
+ ret = vfs_llseek(realfile, offset, whence);
+ ovl_revert_creds(old_cred);
- file->f_pos = fd_file(real)->f_pos;
+ file->f_pos = realfile->f_pos;
ovl_inode_unlock(inode);
- fdput(real);
-
return ret;
}
@@ -231,9 +289,9 @@ static void ovl_file_modified(struct file *file)
ovl_copyattr(file_inode(file));
}
-static void ovl_file_end_write(struct file *file, loff_t pos, ssize_t ret)
+static void ovl_file_end_write(struct kiocb *iocb, ssize_t ret)
{
- ovl_file_modified(file);
+ ovl_file_modified(iocb->ki_filp);
}
static void ovl_file_accessed(struct file *file)
@@ -267,38 +325,32 @@ static void ovl_file_accessed(struct file *file)
static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter)
{
struct file *file = iocb->ki_filp;
- struct fd real;
- ssize_t ret;
+ struct file *realfile;
struct backing_file_ctx ctx = {
.cred = ovl_creds(file_inode(file)->i_sb),
- .user_file = file,
.accessed = ovl_file_accessed,
};
if (!iov_iter_count(iter))
return 0;
- ret = ovl_real_fdget(file, &real);
- if (ret)
- return ret;
-
- ret = backing_file_read_iter(fd_file(real), iter, iocb, iocb->ki_flags,
- &ctx);
- fdput(real);
+ realfile = ovl_real_file(file);
+ if (IS_ERR(realfile))
+ return PTR_ERR(realfile);
- return ret;
+ return backing_file_read_iter(realfile, iter, iocb, iocb->ki_flags,
+ &ctx);
}
static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
{
struct file *file = iocb->ki_filp;
struct inode *inode = file_inode(file);
- struct fd real;
+ struct file *realfile;
ssize_t ret;
int ifl = iocb->ki_flags;
struct backing_file_ctx ctx = {
.cred = ovl_creds(inode->i_sb),
- .user_file = file,
.end_write = ovl_file_end_write,
};
@@ -309,8 +361,9 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
/* Update mode */
ovl_copyattr(inode);
- ret = ovl_real_fdget(file, &real);
- if (ret)
+ realfile = ovl_real_file(file);
+ ret = PTR_ERR(realfile);
+ if (IS_ERR(realfile))
goto out_unlock;
if (!ovl_should_sync(OVL_FS(inode->i_sb)))
@@ -321,8 +374,7 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
* this property in case it is set by the issuer.
*/
ifl &= ~IOCB_DIO_CALLER_COMP;
- ret = backing_file_write_iter(fd_file(real), iter, iocb, ifl, &ctx);
- fdput(real);
+ ret = backing_file_write_iter(realfile, iter, iocb, ifl, &ctx);
out_unlock:
inode_unlock(inode);
@@ -334,20 +386,22 @@ static ssize_t ovl_splice_read(struct file *in, loff_t *ppos,
struct pipe_inode_info *pipe, size_t len,
unsigned int flags)
{
- struct fd real;
+ struct file *realfile;
ssize_t ret;
struct backing_file_ctx ctx = {
.cred = ovl_creds(file_inode(in)->i_sb),
- .user_file = in,
.accessed = ovl_file_accessed,
};
+ struct kiocb iocb;
- ret = ovl_real_fdget(in, &real);
- if (ret)
- return ret;
+ realfile = ovl_real_file(in);
+ if (IS_ERR(realfile))
+ return PTR_ERR(realfile);
- ret = backing_file_splice_read(fd_file(real), ppos, pipe, len, flags, &ctx);
- fdput(real);
+ init_sync_kiocb(&iocb, in);
+ iocb.ki_pos = *ppos;
+ ret = backing_file_splice_read(realfile, &iocb, pipe, len, flags, &ctx);
+ *ppos = iocb.ki_pos;
return ret;
}
@@ -355,7 +409,7 @@ static ssize_t ovl_splice_read(struct file *in, loff_t *ppos,
/*
* Calling iter_file_splice_write() directly from overlay's f_op may deadlock
* due to lock order inversion between pipe->mutex in iter_file_splice_write()
- * and file_start_write(fd_file(real)) in ovl_write_iter().
+ * and file_start_write(realfile) in ovl_write_iter().
*
* So do everything ovl_write_iter() does and call iter_file_splice_write() on
* the real file.
@@ -363,25 +417,28 @@ static ssize_t ovl_splice_read(struct file *in, loff_t *ppos,
static ssize_t ovl_splice_write(struct pipe_inode_info *pipe, struct file *out,
loff_t *ppos, size_t len, unsigned int flags)
{
- struct fd real;
+ struct file *realfile;
struct inode *inode = file_inode(out);
ssize_t ret;
struct backing_file_ctx ctx = {
.cred = ovl_creds(inode->i_sb),
- .user_file = out,
.end_write = ovl_file_end_write,
};
+ struct kiocb iocb;
inode_lock(inode);
/* Update mode */
ovl_copyattr(inode);
- ret = ovl_real_fdget(out, &real);
- if (ret)
+ realfile = ovl_real_file(out);
+ ret = PTR_ERR(realfile);
+ if (IS_ERR(realfile))
goto out_unlock;
- ret = backing_file_splice_write(pipe, fd_file(real), ppos, len, flags, &ctx);
- fdput(real);
+ init_sync_kiocb(&iocb, out);
+ iocb.ki_pos = *ppos;
+ ret = backing_file_splice_write(pipe, realfile, &iocb, len, flags, &ctx);
+ *ppos = iocb.ki_pos;
out_unlock:
inode_unlock(inode);
@@ -391,7 +448,10 @@ out_unlock:
static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
{
- struct fd real;
+ struct dentry *dentry = file_dentry(file);
+ enum ovl_path_type type;
+ struct path upperpath;
+ struct file *upperfile;
const struct cred *old_cred;
int ret;
@@ -399,38 +459,38 @@ static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
if (ret <= 0)
return ret;
- ret = ovl_real_fdget_meta(file, &real, !datasync);
- if (ret)
- return ret;
-
/* Don't sync lower file for fear of receiving EROFS error */
- if (file_inode(fd_file(real)) == ovl_inode_upper(file_inode(file))) {
- old_cred = ovl_override_creds(file_inode(file)->i_sb);
- ret = vfs_fsync_range(fd_file(real), start, end, datasync);
- revert_creds(old_cred);
- }
+ type = ovl_path_type(dentry);
+ if (!OVL_TYPE_UPPER(type) || (datasync && OVL_TYPE_MERGE(type)))
+ return 0;
- fdput(real);
+ ovl_path_upper(dentry, &upperpath);
+ upperfile = ovl_real_file_path(file, &upperpath);
+ if (IS_ERR(upperfile))
+ return PTR_ERR(upperfile);
+
+ old_cred = ovl_override_creds(file_inode(file)->i_sb);
+ ret = vfs_fsync_range(upperfile, start, end, datasync);
+ ovl_revert_creds(old_cred);
return ret;
}
static int ovl_mmap(struct file *file, struct vm_area_struct *vma)
{
- struct file *realfile = file->private_data;
+ struct ovl_file *of = file->private_data;
struct backing_file_ctx ctx = {
.cred = ovl_creds(file_inode(file)->i_sb),
- .user_file = file,
.accessed = ovl_file_accessed,
};
- return backing_file_mmap(realfile, vma, &ctx);
+ return backing_file_mmap(of->realfile, vma, &ctx);
}
static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
{
struct inode *inode = file_inode(file);
- struct fd real;
+ struct file *realfile;
const struct cred *old_cred;
int ret;
@@ -441,19 +501,18 @@ static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len
if (ret)
goto out_unlock;
- ret = ovl_real_fdget(file, &real);
- if (ret)
+ realfile = ovl_real_file(file);
+ ret = PTR_ERR(realfile);
+ if (IS_ERR(realfile))
goto out_unlock;
old_cred = ovl_override_creds(file_inode(file)->i_sb);
- ret = vfs_fallocate(fd_file(real), mode, offset, len);
- revert_creds(old_cred);
+ ret = vfs_fallocate(realfile, mode, offset, len);
+ ovl_revert_creds(old_cred);
/* Update size */
ovl_file_modified(file);
- fdput(real);
-
out_unlock:
inode_unlock(inode);
@@ -462,19 +521,17 @@ out_unlock:
static int ovl_fadvise(struct file *file, loff_t offset, loff_t len, int advice)
{
- struct fd real;
+ struct file *realfile;
const struct cred *old_cred;
int ret;
- ret = ovl_real_fdget(file, &real);
- if (ret)
- return ret;
+ realfile = ovl_real_file(file);
+ if (IS_ERR(realfile))
+ return PTR_ERR(realfile);
old_cred = ovl_override_creds(file_inode(file)->i_sb);
- ret = vfs_fadvise(fd_file(real), offset, len, advice);
- revert_creds(old_cred);
-
- fdput(real);
+ ret = vfs_fadvise(realfile, offset, len, advice);
+ ovl_revert_creds(old_cred);
return ret;
}
@@ -490,7 +547,7 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in,
loff_t len, unsigned int flags, enum ovl_copyop op)
{
struct inode *inode_out = file_inode(file_out);
- struct fd real_in, real_out;
+ struct file *realfile_in, *realfile_out;
const struct cred *old_cred;
loff_t ret;
@@ -503,42 +560,39 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in,
goto out_unlock;
}
- ret = ovl_real_fdget(file_out, &real_out);
- if (ret)
+ realfile_out = ovl_real_file(file_out);
+ ret = PTR_ERR(realfile_out);
+ if (IS_ERR(realfile_out))
goto out_unlock;
- ret = ovl_real_fdget(file_in, &real_in);
- if (ret) {
- fdput(real_out);
+ realfile_in = ovl_real_file(file_in);
+ ret = PTR_ERR(realfile_in);
+ if (IS_ERR(realfile_in))
goto out_unlock;
- }
old_cred = ovl_override_creds(file_inode(file_out)->i_sb);
switch (op) {
case OVL_COPY:
- ret = vfs_copy_file_range(fd_file(real_in), pos_in,
- fd_file(real_out), pos_out, len, flags);
+ ret = vfs_copy_file_range(realfile_in, pos_in,
+ realfile_out, pos_out, len, flags);
break;
case OVL_CLONE:
- ret = vfs_clone_file_range(fd_file(real_in), pos_in,
- fd_file(real_out), pos_out, len, flags);
+ ret = vfs_clone_file_range(realfile_in, pos_in,
+ realfile_out, pos_out, len, flags);
break;
case OVL_DEDUPE:
- ret = vfs_dedupe_file_range_one(fd_file(real_in), pos_in,
- fd_file(real_out), pos_out, len,
+ ret = vfs_dedupe_file_range_one(realfile_in, pos_in,
+ realfile_out, pos_out, len,
flags);
break;
}
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
/* Update size */
ovl_file_modified(file_out);
- fdput(real_in);
- fdput(real_out);
-
out_unlock:
inode_unlock(inode_out);
@@ -582,20 +636,19 @@ static loff_t ovl_remap_file_range(struct file *file_in, loff_t pos_in,
static int ovl_flush(struct file *file, fl_owner_t id)
{
- struct fd real;
+ struct file *realfile;
const struct cred *old_cred;
- int err;
+ int err = 0;
- err = ovl_real_fdget(file, &real);
- if (err)
- return err;
+ realfile = ovl_real_file(file);
+ if (IS_ERR(realfile))
+ return PTR_ERR(realfile);
- if (fd_file(real)->f_op->flush) {
+ if (realfile->f_op->flush) {
old_cred = ovl_override_creds(file_inode(file)->i_sb);
- err = fd_file(real)->f_op->flush(fd_file(real), id);
- revert_creds(old_cred);
+ err = realfile->f_op->flush(realfile, id);
+ ovl_revert_creds(old_cred);
}
- fdput(real);
return err;
}
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
index 8b31f44c12cd..6f0e15f86c21 100644
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -80,7 +80,7 @@ int ovl_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
inode_lock(upperdentry->d_inode);
old_cred = ovl_override_creds(dentry->d_sb);
err = ovl_do_notify_change(ofs, upperdentry, attr);
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
if (!err)
ovl_copyattr(dentry->d_inode);
inode_unlock(upperdentry->d_inode);
@@ -280,7 +280,7 @@ int ovl_getattr(struct mnt_idmap *idmap, const struct path *path,
stat->nlink = dentry->d_inode->i_nlink;
out:
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
return err;
}
@@ -317,7 +317,7 @@ int ovl_permission(struct mnt_idmap *idmap,
mask |= MAY_READ;
}
err = inode_permission(mnt_idmap(realpath.mnt), realinode, mask);
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
return err;
}
@@ -334,7 +334,7 @@ static const char *ovl_get_link(struct dentry *dentry,
old_cred = ovl_override_creds(dentry->d_sb);
p = vfs_get_link(ovl_dentry_real(dentry), done);
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
return p;
}
@@ -469,7 +469,7 @@ struct posix_acl *do_ovl_get_acl(struct mnt_idmap *idmap,
old_cred = ovl_override_creds(inode->i_sb);
acl = ovl_get_acl_path(&realpath, posix_acl_xattr_name(type), noperm);
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
}
return acl;
@@ -498,7 +498,7 @@ static int ovl_set_or_remove_acl(struct dentry *dentry, struct inode *inode,
old_cred = ovl_override_creds(dentry->d_sb);
real_acl = vfs_get_acl(mnt_idmap(realpath.mnt), realdentry,
acl_name);
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
if (IS_ERR(real_acl)) {
err = PTR_ERR(real_acl);
goto out;
@@ -523,7 +523,7 @@ static int ovl_set_or_remove_acl(struct dentry *dentry, struct inode *inode,
err = ovl_do_set_acl(ofs, realdentry, acl_name, acl);
else
err = ovl_do_remove_acl(ofs, realdentry, acl_name);
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
ovl_drop_write(dentry);
/* copy c/mtime */
@@ -600,7 +600,7 @@ static int ovl_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
old_cred = ovl_override_creds(inode->i_sb);
err = realinode->i_op->fiemap(realinode, fieinfo, start, len);
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
return err;
}
@@ -616,8 +616,13 @@ static int ovl_security_fileattr(const struct path *realpath, struct fileattr *f
struct file *file;
unsigned int cmd;
int err;
+ unsigned int flags;
+
+ flags = O_RDONLY;
+ if (force_o_largefile())
+ flags |= O_LARGEFILE;
- file = dentry_open(realpath, O_RDONLY, current_cred());
+ file = dentry_open(realpath, flags, current_cred());
if (IS_ERR(file))
return PTR_ERR(file);
@@ -671,7 +676,7 @@ int ovl_fileattr_set(struct mnt_idmap *idmap,
err = ovl_set_protattr(inode, upperpath.dentry, fa);
if (!err)
err = ovl_real_fileattr_set(&upperpath, fa);
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
ovl_drop_write(dentry);
/*
@@ -733,7 +738,7 @@ int ovl_fileattr_get(struct dentry *dentry, struct fileattr *fa)
old_cred = ovl_override_creds(inode->i_sb);
err = ovl_real_fileattr_get(&realpath, fa);
ovl_fileattr_prot_flags(inode, fa);
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
return err;
}
diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c
index 5764f91d283e..7e27b7d4adee 100644
--- a/fs/overlayfs/namei.c
+++ b/fs/overlayfs/namei.c
@@ -961,7 +961,7 @@ static int ovl_maybe_validate_verity(struct dentry *dentry)
if (err == 0)
ovl_set_flag(OVL_VERIFIED_DIGEST, inode);
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
}
ovl_inode_unlock(inode);
@@ -995,7 +995,7 @@ static int ovl_maybe_lookup_lowerdata(struct dentry *dentry)
old_cred = ovl_override_creds(dentry->d_sb);
err = ovl_lookup_data_layers(dentry, redirect, &datapath);
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
if (err)
goto out_err;
@@ -1342,7 +1342,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
ovl_dentry_init_reval(dentry, upperdentry, OVL_I_E(inode));
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
if (origin_path) {
dput(origin_path->dentry);
kfree(origin_path);
@@ -1366,7 +1366,7 @@ out_put_upper:
kfree(upperredirect);
out:
kfree(d.redirect);
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
return ERR_PTR(err);
}
@@ -1423,7 +1423,7 @@ bool ovl_lower_positive(struct dentry *dentry)
dput(this);
}
}
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
return positive;
}
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index 910dbbb2bb7b..b361f35762be 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -421,6 +421,7 @@ int ovl_want_write(struct dentry *dentry);
void ovl_drop_write(struct dentry *dentry);
struct dentry *ovl_workdir(struct dentry *dentry);
const struct cred *ovl_override_creds(struct super_block *sb);
+void ovl_revert_creds(const struct cred *old_cred);
static inline const struct cred *ovl_creds(struct super_block *sb)
{
@@ -854,6 +855,9 @@ int ovl_real_fileattr_set(const struct path *realpath, struct fileattr *fa);
int ovl_fileattr_get(struct dentry *dentry, struct fileattr *fa);
int ovl_fileattr_set(struct mnt_idmap *idmap,
struct dentry *dentry, struct fileattr *fa);
+struct ovl_file;
+struct ovl_file *ovl_file_alloc(struct file *realfile);
+void ovl_file_free(struct ovl_file *of);
/* copy_up.c */
int ovl_copy_up(struct dentry *dentry);
diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c
index 0ca8af060b0c..881ec5592da5 100644
--- a/fs/overlayfs/readdir.c
+++ b/fs/overlayfs/readdir.c
@@ -290,7 +290,7 @@ static int ovl_check_whiteouts(const struct path *path, struct ovl_readdir_data
}
inode_unlock(dir->d_inode);
}
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
return err;
}
@@ -808,7 +808,7 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx)
}
err = 0;
out:
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
return err;
}
@@ -860,7 +860,7 @@ static struct file *ovl_dir_open_realfile(const struct file *file,
old_cred = ovl_override_creds(file_inode(file)->i_sb);
res = ovl_path_open(realpath, O_RDONLY | (file->f_flags & O_LARGEFILE));
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
return res;
}
@@ -987,7 +987,7 @@ int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list)
old_cred = ovl_override_creds(dentry->d_sb);
err = ovl_dir_read_merged(dentry, list, &root);
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
if (err)
return err;
diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c
index edc9216f6e27..9aa7493b1e10 100644
--- a/fs/overlayfs/util.c
+++ b/fs/overlayfs/util.c
@@ -65,7 +65,12 @@ const struct cred *ovl_override_creds(struct super_block *sb)
{
struct ovl_fs *ofs = OVL_FS(sb);
- return override_creds(ofs->creator_cred);
+ return override_creds_light(ofs->creator_cred);
+}
+
+void ovl_revert_creds(const struct cred *old_cred)
+{
+ revert_creds_light(old_cred);
}
/*
@@ -197,6 +202,9 @@ void ovl_dentry_init_flags(struct dentry *dentry, struct dentry *upperdentry,
bool ovl_dentry_weird(struct dentry *dentry)
{
+ if (!d_can_lookup(dentry) && !d_is_file(dentry) && !d_is_symlink(dentry))
+ return true;
+
return dentry->d_flags & (DCACHE_NEED_AUTOMOUNT |
DCACHE_MANAGE_TRANSIT |
DCACHE_OP_HASH |
@@ -1178,7 +1186,7 @@ int ovl_nlink_start(struct dentry *dentry)
* value relative to the upper inode nlink in an upper inode xattr.
*/
err = ovl_set_nlink_upper(dentry);
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
if (err)
goto out_drop_write;
@@ -1203,7 +1211,7 @@ void ovl_nlink_end(struct dentry *dentry)
old_cred = ovl_override_creds(dentry->d_sb);
ovl_cleanup_index(dentry);
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
}
ovl_inode_unlock(inode);
diff --git a/fs/overlayfs/xattrs.c b/fs/overlayfs/xattrs.c
index 383978e4663c..88055deca936 100644
--- a/fs/overlayfs/xattrs.c
+++ b/fs/overlayfs/xattrs.c
@@ -47,7 +47,7 @@ static int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char
ovl_path_lower(dentry, &realpath);
old_cred = ovl_override_creds(dentry->d_sb);
err = vfs_getxattr(mnt_idmap(realpath.mnt), realdentry, name, NULL, 0);
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
if (err < 0)
goto out;
}
@@ -72,7 +72,7 @@ static int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char
WARN_ON(flags != XATTR_REPLACE);
err = ovl_do_removexattr(ofs, realdentry, name);
}
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
ovl_drop_write(dentry);
/* copy c/mtime */
@@ -91,7 +91,7 @@ static int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char
ovl_i_path_real(inode, &realpath);
old_cred = ovl_override_creds(dentry->d_sb);
res = vfs_getxattr(mnt_idmap(realpath.mnt), realpath.dentry, name, value, size);
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
return res;
}
@@ -121,7 +121,7 @@ ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
old_cred = ovl_override_creds(dentry->d_sb);
res = vfs_listxattr(realdentry, list, size);
- revert_creds(old_cred);
+ ovl_revert_creds(old_cred);
if (res <= 0 || size == 0)
return res;
@@ -268,4 +268,3 @@ const struct xattr_handler * const *ovl_xattr_handlers(struct ovl_fs *ofs)
return ofs->config.userxattr ? ovl_user_xattr_handlers :
ovl_trusted_xattr_handlers;
}
-