mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2024-12-28 00:33:16 +00:00
overlayfs updates for 6.13
-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE9zuTYTs0RXF+Ke33EVvVyTe/1WoFAmc90jsACgkQEVvVyTe/ 1Wol0A//RhzFCG8geR7Grbptp40CUm9kVISvkr50mPBdvVk3jX9WvH9m/10qapGP tcGHSdHt+q5qabqutKLmQRiFbwpGEaBMaFOe7JH8na8xWvmSa3p7sJC5kLByS3rm D2F+cVx3Di7MTscz/Ma724bHdHOUO5RbDuMIcjp7uXRvaNWJ0uZg5xWlBKsNa3h8 DbNSYi5ICihLYpUxI9NglHZ6iqcS2jHsUHSAw52/GJ2Zon1LAAmKoSn6s7hZ27ZJ f8Rv5fFuYmkRV7nYo/gjLY1gt7KXZFcfUtMT05yd7zcnqDayKEFXEiwI/Bz5fXZL HmZpOP4RV2M9B8HzhReVR/yG8gZaaUezX+aVQp7plZSc73GhMdFFd1bUyjgJ4Lzf C2BlBMWafc/Zc7a7r0+X5577i34nED8lGuVMEdYMtjSjstpzIP+1Wlzn2cGi4+5K VAb+kEravjP9ck7YrmbruRYfVhDaE37BDs4XML4S8gzcZgdaTcEMyGw1ifEhvPjA vLbRs24a5VO7/cKlks7PWS6i9uExaz7g4re0jUPwUuc+nS+Hv+y8kLSPqLS4CtNY MxhS2IhKK5gp1Z9XGpLsak+ancTYLSV0OJ15qsAChpqoqSG5Xd9Lt4CWACnF33Ea ny8z5QpOAHWVb97k6xaEvu/r0dl+PHdG7vfb0MNhXaajNF8SKiU= =pgoX -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
e7675238b9
@ -80,7 +80,7 @@ struct backing_aio {
|
||||
refcount_t ref;
|
||||
struct kiocb *orig_iocb;
|
||||
/* used for aio completion */
|
||||
void (*end_write)(struct file *, loff_t, ssize_t);
|
||||
void (*end_write)(struct kiocb *iocb, ssize_t);
|
||||
struct work_struct work;
|
||||
long res;
|
||||
};
|
||||
@ -108,10 +108,10 @@ static void backing_aio_cleanup(struct backing_aio *aio, long res)
|
||||
struct kiocb *iocb = &aio->iocb;
|
||||
struct kiocb *orig_iocb = aio->orig_iocb;
|
||||
|
||||
if (aio->end_write)
|
||||
aio->end_write(orig_iocb->ki_filp, iocb->ki_pos, res);
|
||||
|
||||
orig_iocb->ki_pos = iocb->ki_pos;
|
||||
if (aio->end_write)
|
||||
aio->end_write(orig_iocb, res);
|
||||
|
||||
backing_aio_put(aio);
|
||||
}
|
||||
|
||||
@ -176,7 +176,7 @@ ssize_t backing_file_read_iter(struct file *file, struct iov_iter *iter,
|
||||
!(file->f_mode & FMODE_CAN_ODIRECT))
|
||||
return -EINVAL;
|
||||
|
||||
old_cred = override_creds(ctx->cred);
|
||||
old_cred = override_creds_light(ctx->cred);
|
||||
if (is_sync_kiocb(iocb)) {
|
||||
rwf_t rwf = iocb_to_rw_flags(flags);
|
||||
|
||||
@ -197,10 +197,10 @@ ssize_t backing_file_read_iter(struct file *file, struct iov_iter *iter,
|
||||
backing_aio_cleanup(aio, ret);
|
||||
}
|
||||
out:
|
||||
revert_creds(old_cred);
|
||||
revert_creds_light(old_cred);
|
||||
|
||||
if (ctx->accessed)
|
||||
ctx->accessed(ctx->user_file);
|
||||
ctx->accessed(iocb->ki_filp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -219,7 +219,7 @@ ssize_t backing_file_write_iter(struct file *file, struct iov_iter *iter,
|
||||
if (!iov_iter_count(iter))
|
||||
return 0;
|
||||
|
||||
ret = file_remove_privs(ctx->user_file);
|
||||
ret = file_remove_privs(iocb->ki_filp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -233,13 +233,13 @@ ssize_t backing_file_write_iter(struct file *file, struct iov_iter *iter,
|
||||
*/
|
||||
flags &= ~IOCB_DIO_CALLER_COMP;
|
||||
|
||||
old_cred = override_creds(ctx->cred);
|
||||
old_cred = override_creds_light(ctx->cred);
|
||||
if (is_sync_kiocb(iocb)) {
|
||||
rwf_t rwf = iocb_to_rw_flags(flags);
|
||||
|
||||
ret = vfs_iter_write(file, iter, &iocb->ki_pos, rwf);
|
||||
if (ctx->end_write)
|
||||
ctx->end_write(ctx->user_file, iocb->ki_pos, ret);
|
||||
ctx->end_write(iocb, ret);
|
||||
} else {
|
||||
struct backing_aio *aio;
|
||||
|
||||
@ -264,13 +264,13 @@ ssize_t backing_file_write_iter(struct file *file, struct iov_iter *iter,
|
||||
backing_aio_cleanup(aio, ret);
|
||||
}
|
||||
out:
|
||||
revert_creds(old_cred);
|
||||
revert_creds_light(old_cred);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(backing_file_write_iter);
|
||||
|
||||
ssize_t backing_file_splice_read(struct file *in, loff_t *ppos,
|
||||
ssize_t backing_file_splice_read(struct file *in, struct kiocb *iocb,
|
||||
struct pipe_inode_info *pipe, size_t len,
|
||||
unsigned int flags,
|
||||
struct backing_file_ctx *ctx)
|
||||
@ -281,20 +281,20 @@ ssize_t backing_file_splice_read(struct file *in, loff_t *ppos,
|
||||
if (WARN_ON_ONCE(!(in->f_mode & FMODE_BACKING)))
|
||||
return -EIO;
|
||||
|
||||
old_cred = override_creds(ctx->cred);
|
||||
ret = vfs_splice_read(in, ppos, pipe, len, flags);
|
||||
revert_creds(old_cred);
|
||||
old_cred = override_creds_light(ctx->cred);
|
||||
ret = vfs_splice_read(in, &iocb->ki_pos, pipe, len, flags);
|
||||
revert_creds_light(old_cred);
|
||||
|
||||
if (ctx->accessed)
|
||||
ctx->accessed(ctx->user_file);
|
||||
ctx->accessed(iocb->ki_filp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(backing_file_splice_read);
|
||||
|
||||
ssize_t backing_file_splice_write(struct pipe_inode_info *pipe,
|
||||
struct file *out, loff_t *ppos, size_t len,
|
||||
unsigned int flags,
|
||||
struct file *out, struct kiocb *iocb,
|
||||
size_t len, unsigned int flags,
|
||||
struct backing_file_ctx *ctx)
|
||||
{
|
||||
const struct cred *old_cred;
|
||||
@ -306,18 +306,18 @@ ssize_t backing_file_splice_write(struct pipe_inode_info *pipe,
|
||||
if (!out->f_op->splice_write)
|
||||
return -EINVAL;
|
||||
|
||||
ret = file_remove_privs(ctx->user_file);
|
||||
ret = file_remove_privs(iocb->ki_filp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
old_cred = override_creds(ctx->cred);
|
||||
old_cred = override_creds_light(ctx->cred);
|
||||
file_start_write(out);
|
||||
ret = out->f_op->splice_write(pipe, out, ppos, len, flags);
|
||||
ret = out->f_op->splice_write(pipe, out, &iocb->ki_pos, len, flags);
|
||||
file_end_write(out);
|
||||
revert_creds(old_cred);
|
||||
revert_creds_light(old_cred);
|
||||
|
||||
if (ctx->end_write)
|
||||
ctx->end_write(ctx->user_file, ppos ? *ppos : 0, ret);
|
||||
ctx->end_write(iocb, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -329,8 +329,7 @@ int backing_file_mmap(struct file *file, struct vm_area_struct *vma,
|
||||
const struct cred *old_cred;
|
||||
int ret;
|
||||
|
||||
if (WARN_ON_ONCE(!(file->f_mode & FMODE_BACKING)) ||
|
||||
WARN_ON_ONCE(ctx->user_file != vma->vm_file))
|
||||
if (WARN_ON_ONCE(!(file->f_mode & FMODE_BACKING)))
|
||||
return -EIO;
|
||||
|
||||
if (!file->f_op->mmap)
|
||||
@ -338,12 +337,12 @@ int backing_file_mmap(struct file *file, struct vm_area_struct *vma,
|
||||
|
||||
vma_set_file(vma, file);
|
||||
|
||||
old_cred = override_creds(ctx->cred);
|
||||
old_cred = override_creds_light(ctx->cred);
|
||||
ret = call_mmap(vma->vm_file, vma);
|
||||
revert_creds(old_cred);
|
||||
revert_creds_light(old_cred);
|
||||
|
||||
if (ctx->accessed)
|
||||
ctx->accessed(ctx->user_file);
|
||||
ctx->accessed(vma->vm_file);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -18,11 +18,11 @@ static void fuse_file_accessed(struct file *file)
|
||||
fuse_invalidate_atime(inode);
|
||||
}
|
||||
|
||||
static void fuse_passthrough_end_write(struct file *file, loff_t pos, ssize_t ret)
|
||||
static void fuse_passthrough_end_write(struct kiocb *iocb, ssize_t ret)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct inode *inode = file_inode(iocb->ki_filp);
|
||||
|
||||
fuse_write_update_attr(inode, pos, ret);
|
||||
fuse_write_update_attr(inode, iocb->ki_pos, ret);
|
||||
}
|
||||
|
||||
ssize_t fuse_passthrough_read_iter(struct kiocb *iocb, struct iov_iter *iter)
|
||||
@ -34,7 +34,6 @@ ssize_t fuse_passthrough_read_iter(struct kiocb *iocb, struct iov_iter *iter)
|
||||
ssize_t ret;
|
||||
struct backing_file_ctx ctx = {
|
||||
.cred = ff->cred,
|
||||
.user_file = file,
|
||||
.accessed = fuse_file_accessed,
|
||||
};
|
||||
|
||||
@ -62,7 +61,6 @@ ssize_t fuse_passthrough_write_iter(struct kiocb *iocb,
|
||||
ssize_t ret;
|
||||
struct backing_file_ctx ctx = {
|
||||
.cred = ff->cred,
|
||||
.user_file = file,
|
||||
.end_write = fuse_passthrough_end_write,
|
||||
};
|
||||
|
||||
@ -88,15 +86,20 @@ ssize_t fuse_passthrough_splice_read(struct file *in, loff_t *ppos,
|
||||
struct file *backing_file = fuse_file_passthrough(ff);
|
||||
struct backing_file_ctx ctx = {
|
||||
.cred = ff->cred,
|
||||
.user_file = in,
|
||||
.accessed = fuse_file_accessed,
|
||||
};
|
||||
struct kiocb iocb;
|
||||
ssize_t ret;
|
||||
|
||||
pr_debug("%s: backing_file=0x%p, pos=%lld, len=%zu, flags=0x%x\n", __func__,
|
||||
backing_file, ppos ? *ppos : 0, len, flags);
|
||||
backing_file, *ppos, len, flags);
|
||||
|
||||
return backing_file_splice_read(backing_file, ppos, pipe, len, flags,
|
||||
&ctx);
|
||||
init_sync_kiocb(&iocb, in);
|
||||
iocb.ki_pos = *ppos;
|
||||
ret = backing_file_splice_read(backing_file, &iocb, pipe, len, flags, &ctx);
|
||||
*ppos = iocb.ki_pos;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t fuse_passthrough_splice_write(struct pipe_inode_info *pipe,
|
||||
@ -109,16 +112,18 @@ ssize_t fuse_passthrough_splice_write(struct pipe_inode_info *pipe,
|
||||
ssize_t ret;
|
||||
struct backing_file_ctx ctx = {
|
||||
.cred = ff->cred,
|
||||
.user_file = out,
|
||||
.end_write = fuse_passthrough_end_write,
|
||||
};
|
||||
struct kiocb iocb;
|
||||
|
||||
pr_debug("%s: backing_file=0x%p, pos=%lld, len=%zu, flags=0x%x\n", __func__,
|
||||
backing_file, ppos ? *ppos : 0, len, flags);
|
||||
backing_file, *ppos, len, flags);
|
||||
|
||||
inode_lock(inode);
|
||||
ret = backing_file_splice_write(pipe, backing_file, ppos, len, flags,
|
||||
&ctx);
|
||||
init_sync_kiocb(&iocb, out);
|
||||
iocb.ki_pos = *ppos;
|
||||
ret = backing_file_splice_write(pipe, backing_file, &iocb, len, flags, &ctx);
|
||||
*ppos = iocb.ki_pos;
|
||||
inode_unlock(inode);
|
||||
|
||||
return ret;
|
||||
@ -130,7 +135,6 @@ ssize_t fuse_passthrough_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
struct file *backing_file = fuse_file_passthrough(ff);
|
||||
struct backing_file_ctx ctx = {
|
||||
.cred = ff->cred,
|
||||
.user_file = file,
|
||||
.accessed = fuse_file_accessed,
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -553,15 +553,17 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
|
||||
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 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
|
||||
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 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
|
||||
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:
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
static int ovl_real_fdget(const struct file *file, struct fd *real)
|
||||
{
|
||||
if (d_is_dir(file_dentry(file))) {
|
||||
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 (err)
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
return ovl_real_fdget_meta(file, real, false);
|
||||
return realfile;
|
||||
}
|
||||
|
||||
static struct file *ovl_real_file(const struct file *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 (WARN_ON_ONCE(!f))
|
||||
return ERR_PTR(-EIO);
|
||||
return f;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
realfile = ovl_real_file(file);
|
||||
if (IS_ERR(realfile))
|
||||
return PTR_ERR(realfile);
|
||||
|
||||
ret = backing_file_read_iter(fd_file(real), iter, iocb, iocb->ki_flags,
|
||||
&ctx);
|
||||
fdput(real);
|
||||
|
||||
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 @@ static ssize_t ovl_splice_write(struct pipe_inode_info *pipe, struct file *out,
|
||||
|
||||
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 @@ static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
file = dentry_open(realpath, O_RDONLY, current_cred());
|
||||
flags = O_RDONLY;
|
||||
if (force_o_largefile())
|
||||
flags |= O_LARGEFILE;
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
||||
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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -14,9 +14,8 @@
|
||||
|
||||
struct backing_file_ctx {
|
||||
const struct cred *cred;
|
||||
struct file *user_file;
|
||||
void (*accessed)(struct file *);
|
||||
void (*end_write)(struct file *, loff_t, ssize_t);
|
||||
void (*accessed)(struct file *file);
|
||||
void (*end_write)(struct kiocb *iocb, ssize_t);
|
||||
};
|
||||
|
||||
struct file *backing_file_open(const struct path *user_path, int flags,
|
||||
@ -31,13 +30,13 @@ ssize_t backing_file_read_iter(struct file *file, struct iov_iter *iter,
|
||||
ssize_t backing_file_write_iter(struct file *file, struct iov_iter *iter,
|
||||
struct kiocb *iocb, int flags,
|
||||
struct backing_file_ctx *ctx);
|
||||
ssize_t backing_file_splice_read(struct file *in, loff_t *ppos,
|
||||
ssize_t backing_file_splice_read(struct file *in, struct kiocb *iocb,
|
||||
struct pipe_inode_info *pipe, size_t len,
|
||||
unsigned int flags,
|
||||
struct backing_file_ctx *ctx);
|
||||
ssize_t backing_file_splice_write(struct pipe_inode_info *pipe,
|
||||
struct file *out, loff_t *ppos, size_t len,
|
||||
unsigned int flags,
|
||||
struct file *out, struct kiocb *iocb,
|
||||
size_t len, unsigned int flags,
|
||||
struct backing_file_ctx *ctx);
|
||||
int backing_file_mmap(struct file *file, struct vm_area_struct *vma,
|
||||
struct backing_file_ctx *ctx);
|
||||
|
@ -172,6 +172,24 @@ static inline bool cap_ambient_invariant_ok(const struct cred *cred)
|
||||
cred->cap_inheritable));
|
||||
}
|
||||
|
||||
/*
|
||||
* Override creds without bumping reference count. Caller must ensure
|
||||
* reference remains valid or has taken reference. Almost always not the
|
||||
* interface you want. Use override_creds()/revert_creds() instead.
|
||||
*/
|
||||
static inline const struct cred *override_creds_light(const struct cred *override_cred)
|
||||
{
|
||||
const struct cred *old = current->cred;
|
||||
|
||||
rcu_assign_pointer(current->cred, override_cred);
|
||||
return old;
|
||||
}
|
||||
|
||||
static inline void revert_creds_light(const struct cred *revert_cred)
|
||||
{
|
||||
rcu_assign_pointer(current->cred, revert_cred);
|
||||
}
|
||||
|
||||
/**
|
||||
* get_new_cred_many - Get references on a new set of credentials
|
||||
* @cred: The new credentials to reference
|
||||
|
@ -485,7 +485,7 @@ EXPORT_SYMBOL(abort_creds);
|
||||
*/
|
||||
const struct cred *override_creds(const struct cred *new)
|
||||
{
|
||||
const struct cred *old = current->cred;
|
||||
const struct cred *old;
|
||||
|
||||
kdebug("override_creds(%p{%ld})", new,
|
||||
atomic_long_read(&new->usage));
|
||||
@ -499,7 +499,7 @@ const struct cred *override_creds(const struct cred *new)
|
||||
* visible to other threads under RCU.
|
||||
*/
|
||||
get_new_cred((struct cred *)new);
|
||||
rcu_assign_pointer(current->cred, new);
|
||||
old = override_creds_light(new);
|
||||
|
||||
kdebug("override_creds() = %p{%ld}", old,
|
||||
atomic_long_read(&old->usage));
|
||||
@ -521,7 +521,7 @@ void revert_creds(const struct cred *old)
|
||||
kdebug("revert_creds(%p{%ld})", old,
|
||||
atomic_long_read(&old->usage));
|
||||
|
||||
rcu_assign_pointer(current->cred, old);
|
||||
revert_creds_light(old);
|
||||
put_cred(override);
|
||||
}
|
||||
EXPORT_SYMBOL(revert_creds);
|
||||
|
Loading…
Reference in New Issue
Block a user