mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
Merge tag 'vfs-6.19-rc1.ovl' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
Pull overlayfs cred guard conversion from Christian Brauner:
"This converts all of overlayfs to use credential guards, eliminating
manual credential management throughout the filesystem.
Credential guard conversion:
- Convert all of overlayfs to use credential guards, replacing the
manual ovl_override_creds()/ovl_revert_creds() pattern with scoped
guards.
This makes credential handling visually explicit and eliminates a
class of potential bugs from mismatched override/revert calls.
(1) Basic credential guard (with_ovl_creds)
(2) Creator credential guard (ovl_override_creator_creds):
Introduced a specialized guard for file creation operations
that handles the two-phase credential override (mounter
credentials, then fs{g,u}id override). The new pattern is much
clearer:
with_ovl_creds(dentry->d_sb) {
scoped_class(prepare_creds_ovl, cred, dentry, inode, mode) {
if (IS_ERR(cred))
return PTR_ERR(cred);
/* creation operations */
}
}
(3) Copy-up credential guard (ovl_cu_creds):
Introduced a specialized guard for copy-up operations,
simplifying the previous struct ovl_cu_creds helper and
associated functions.
Ported ovl_copy_up_workdir() and ovl_copy_up_tmpfile() to this
pattern.
Cleanups:
- Remove ovl_revert_creds() after all callers converted to guards
- Remove struct ovl_cu_creds and associated functions
- Drop ovl_setup_cred_for_create() after conversion
- Refactor ovl_fill_super(), ovl_lookup(), ovl_iterate(),
ovl_rename() for cleaner credential guard scope
- Introduce struct ovl_renamedata to simplify rename handling
- Don't override credentials for ovl_check_whiteouts() (unnecessary)
- Remove unneeded semicolon"
* tag 'vfs-6.19-rc1.ovl' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: (54 commits)
ovl: remove unneeded semicolon
ovl: remove struct ovl_cu_creds and associated functions
ovl: port ovl_copy_up_tmpfile() to cred guard
ovl: mark *_cu_creds() as unused temporarily
ovl: port ovl_copy_up_workdir() to cred guard
ovl: add copy up credential guard
ovl: drop ovl_setup_cred_for_create()
ovl: port ovl_create_or_link() to new ovl_override_creator_creds cleanup guard
ovl: mark ovl_setup_cred_for_create() as unused temporarily
ovl: reflow ovl_create_or_link()
ovl: port ovl_create_tmpfile() to new ovl_override_creator_creds cleanup guard
ovl: add ovl_override_creator_creds cred guard
ovl: remove ovl_revert_creds()
ovl: port ovl_fill_super() to cred guard
ovl: refactor ovl_fill_super()
ovl: port ovl_lower_positive() to cred guard
ovl: port ovl_lookup() to cred guard
ovl: refactor ovl_lookup()
ovl: port ovl_copyfile() to cred guard
ovl: port ovl_rename() to cred guard
...
This commit is contained in:
@@ -724,34 +724,33 @@ static int ovl_copy_up_metadata(struct ovl_copy_up_ctx *c, struct dentry *temp)
|
||||
return err;
|
||||
}
|
||||
|
||||
struct ovl_cu_creds {
|
||||
const struct cred *old;
|
||||
struct cred *new;
|
||||
};
|
||||
|
||||
static int ovl_prep_cu_creds(struct dentry *dentry, struct ovl_cu_creds *cc)
|
||||
static const struct cred *ovl_prepare_copy_up_creds(struct dentry *dentry)
|
||||
{
|
||||
struct cred *copy_up_cred = NULL;
|
||||
int err;
|
||||
|
||||
cc->old = cc->new = NULL;
|
||||
err = security_inode_copy_up(dentry, &cc->new);
|
||||
err = security_inode_copy_up(dentry, ©_up_cred);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return ERR_PTR(err);
|
||||
|
||||
if (cc->new)
|
||||
cc->old = override_creds(cc->new);
|
||||
if (!copy_up_cred)
|
||||
return NULL;
|
||||
|
||||
return 0;
|
||||
return override_creds(copy_up_cred);
|
||||
}
|
||||
|
||||
static void ovl_revert_cu_creds(struct ovl_cu_creds *cc)
|
||||
static void ovl_revert_copy_up_creds(const struct cred *orig_cred)
|
||||
{
|
||||
if (cc->new) {
|
||||
revert_creds(cc->old);
|
||||
put_cred(cc->new);
|
||||
}
|
||||
const struct cred *copy_up_cred;
|
||||
|
||||
copy_up_cred = revert_creds(orig_cred);
|
||||
put_cred(copy_up_cred);
|
||||
}
|
||||
|
||||
DEFINE_CLASS(copy_up_creds, const struct cred *,
|
||||
if (!IS_ERR_OR_NULL(_T)) ovl_revert_copy_up_creds(_T),
|
||||
ovl_prepare_copy_up_creds(dentry), struct dentry *dentry)
|
||||
|
||||
/*
|
||||
* Copyup using workdir to prepare temp file. Used when copying up directories,
|
||||
* special files or when upper fs doesn't support O_TMPFILE.
|
||||
@@ -763,7 +762,6 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
|
||||
struct path path = { .mnt = ovl_upper_mnt(ofs) };
|
||||
struct renamedata rd = {};
|
||||
struct dentry *temp;
|
||||
struct ovl_cu_creds cc;
|
||||
int err;
|
||||
struct ovl_cattr cattr = {
|
||||
/* Can't properly set mode on creation because of the umask */
|
||||
@@ -772,14 +770,14 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
|
||||
.link = c->link
|
||||
};
|
||||
|
||||
err = ovl_prep_cu_creds(c->dentry, &cc);
|
||||
if (err)
|
||||
return err;
|
||||
scoped_class(copy_up_creds, copy_up_creds, c->dentry) {
|
||||
if (IS_ERR(copy_up_creds))
|
||||
return PTR_ERR(copy_up_creds);
|
||||
|
||||
ovl_start_write(c->dentry);
|
||||
temp = ovl_create_temp(ofs, c->workdir, &cattr);
|
||||
ovl_end_write(c->dentry);
|
||||
ovl_revert_cu_creds(&cc);
|
||||
ovl_start_write(c->dentry);
|
||||
temp = ovl_create_temp(ofs, c->workdir, &cattr);
|
||||
ovl_end_write(c->dentry);
|
||||
}
|
||||
|
||||
if (IS_ERR(temp))
|
||||
return PTR_ERR(temp);
|
||||
@@ -857,17 +855,17 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
|
||||
struct inode *udir = d_inode(c->destdir);
|
||||
struct dentry *temp, *upper;
|
||||
struct file *tmpfile;
|
||||
struct ovl_cu_creds cc;
|
||||
int err;
|
||||
|
||||
err = ovl_prep_cu_creds(c->dentry, &cc);
|
||||
if (err)
|
||||
return err;
|
||||
scoped_class(copy_up_creds, copy_up_creds, c->dentry) {
|
||||
if (IS_ERR(copy_up_creds))
|
||||
return PTR_ERR(copy_up_creds);
|
||||
|
||||
ovl_start_write(c->dentry);
|
||||
tmpfile = ovl_do_tmpfile(ofs, c->workdir, c->stat.mode);
|
||||
ovl_end_write(c->dentry);
|
||||
}
|
||||
|
||||
ovl_start_write(c->dentry);
|
||||
tmpfile = ovl_do_tmpfile(ofs, c->workdir, c->stat.mode);
|
||||
ovl_end_write(c->dentry);
|
||||
ovl_revert_cu_creds(&cc);
|
||||
if (IS_ERR(tmpfile))
|
||||
return PTR_ERR(tmpfile);
|
||||
|
||||
@@ -1203,7 +1201,6 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
|
||||
static int ovl_copy_up_flags(struct dentry *dentry, int flags)
|
||||
{
|
||||
int err = 0;
|
||||
const struct cred *old_cred;
|
||||
bool disconnected = (dentry->d_flags & DCACHE_DISCONNECTED);
|
||||
|
||||
/*
|
||||
@@ -1223,7 +1220,6 @@ static int ovl_copy_up_flags(struct dentry *dentry, int flags)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
while (!err) {
|
||||
struct dentry *next;
|
||||
struct dentry *parent = NULL;
|
||||
@@ -1243,12 +1239,12 @@ static int ovl_copy_up_flags(struct dentry *dentry, int flags)
|
||||
next = parent;
|
||||
}
|
||||
|
||||
err = ovl_copy_up_one(parent, next, flags);
|
||||
with_ovl_creds(dentry->d_sb)
|
||||
err = ovl_copy_up_one(parent, next, flags);
|
||||
|
||||
dput(parent);
|
||||
dput(next);
|
||||
}
|
||||
ovl_revert_creds(old_cred);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ static struct dentry *ovl_whiteout(struct ovl_fs *ofs)
|
||||
whiteout = dget(link);
|
||||
end_creating(link);
|
||||
if (!err)
|
||||
return whiteout;;
|
||||
return whiteout;
|
||||
|
||||
if (err != -EMLINK) {
|
||||
pr_warn("Failed to link whiteout - disabling whiteout inode sharing(nlink=%u, err=%u)\n",
|
||||
@@ -581,59 +581,69 @@ out_cleanup_unlocked:
|
||||
goto out_dput;
|
||||
}
|
||||
|
||||
static const struct cred *ovl_setup_cred_for_create(struct dentry *dentry,
|
||||
struct inode *inode,
|
||||
umode_t mode,
|
||||
const struct cred *old_cred)
|
||||
static const struct cred *ovl_override_creator_creds(struct dentry *dentry, struct inode *inode, umode_t mode)
|
||||
{
|
||||
int err;
|
||||
struct cred *override_cred;
|
||||
|
||||
override_cred = prepare_creds();
|
||||
if (WARN_ON_ONCE(current->cred != ovl_creds(dentry->d_sb)))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
CLASS(prepare_creds, override_cred)();
|
||||
if (!override_cred)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
override_cred->fsuid = inode->i_uid;
|
||||
override_cred->fsgid = inode->i_gid;
|
||||
|
||||
err = security_dentry_create_files_as(dentry, mode, &dentry->d_name,
|
||||
old_cred, override_cred);
|
||||
if (err) {
|
||||
put_cred(override_cred);
|
||||
current->cred, override_cred);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
/*
|
||||
* Caller is going to match this with revert_creds() and drop
|
||||
* referenec on the returned creds.
|
||||
* We must be called with creator creds already, otherwise we risk
|
||||
* leaking creds.
|
||||
*/
|
||||
old_cred = override_creds(override_cred);
|
||||
WARN_ON_ONCE(old_cred != ovl_creds(dentry->d_sb));
|
||||
return override_creds(no_free_ptr(override_cred));
|
||||
}
|
||||
|
||||
return override_cred;
|
||||
static void ovl_revert_creator_creds(const struct cred *old_cred)
|
||||
{
|
||||
const struct cred *override_cred;
|
||||
|
||||
override_cred = revert_creds(old_cred);
|
||||
put_cred(override_cred);
|
||||
}
|
||||
|
||||
DEFINE_CLASS(ovl_override_creator_creds,
|
||||
const struct cred *,
|
||||
if (!IS_ERR_OR_NULL(_T)) ovl_revert_creator_creds(_T),
|
||||
ovl_override_creator_creds(dentry, inode, mode),
|
||||
struct dentry *dentry, struct inode *inode, umode_t mode)
|
||||
|
||||
static int ovl_create_handle_whiteouts(struct dentry *dentry,
|
||||
struct inode *inode,
|
||||
struct ovl_cattr *attr)
|
||||
{
|
||||
if (!ovl_dentry_is_whiteout(dentry))
|
||||
return ovl_create_upper(dentry, inode, attr);
|
||||
|
||||
return ovl_create_over_whiteout(dentry, inode, attr);
|
||||
}
|
||||
|
||||
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, *new_cred = NULL;
|
||||
struct dentry *parent = dentry->d_parent;
|
||||
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
with_ovl_creds(dentry->d_sb) {
|
||||
/*
|
||||
* When linking a file with copy up origin into a new parent, mark the
|
||||
* new parent dir "impure".
|
||||
*/
|
||||
if (origin) {
|
||||
err = ovl_set_impure(parent, ovl_dentry_upper(parent));
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* When linking a file with copy up origin into a new parent, mark the
|
||||
* new parent dir "impure".
|
||||
*/
|
||||
if (origin) {
|
||||
err = ovl_set_impure(parent, ovl_dentry_upper(parent));
|
||||
if (err)
|
||||
goto out_revert_creds;
|
||||
}
|
||||
|
||||
if (!attr->hardlink) {
|
||||
/*
|
||||
* In the creation cases(create, mkdir, mknod, symlink),
|
||||
* ovl should transfer current's fs{u,g}id to underlying
|
||||
@@ -647,23 +657,16 @@ 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.
|
||||
*/
|
||||
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 (attr->hardlink)
|
||||
return ovl_create_handle_whiteouts(dentry, inode, attr);
|
||||
|
||||
scoped_class(ovl_override_creator_creds, cred, dentry, inode, attr->mode) {
|
||||
if (IS_ERR(cred))
|
||||
return PTR_ERR(cred);
|
||||
return ovl_create_handle_whiteouts(dentry, inode, attr);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ovl_dentry_is_whiteout(dentry))
|
||||
err = ovl_create_upper(dentry, inode, attr);
|
||||
else
|
||||
err = ovl_create_over_whiteout(dentry, inode, attr);
|
||||
|
||||
out_revert_creds:
|
||||
ovl_revert_creds(old_cred);
|
||||
put_cred(new_cred);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -739,14 +742,8 @@ static int ovl_symlink(struct mnt_idmap *idmap, struct inode *dir,
|
||||
|
||||
static int ovl_set_link_redirect(struct dentry *dentry)
|
||||
{
|
||||
const struct cred *old_cred;
|
||||
int err;
|
||||
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
err = ovl_set_redirect(dentry, false);
|
||||
ovl_revert_creds(old_cred);
|
||||
|
||||
return err;
|
||||
with_ovl_creds(dentry->d_sb)
|
||||
return ovl_set_redirect(dentry, false);
|
||||
}
|
||||
|
||||
static int ovl_link(struct dentry *old, struct inode *newdir,
|
||||
@@ -921,7 +918,6 @@ static void ovl_drop_nlink(struct dentry *dentry)
|
||||
static int ovl_do_remove(struct dentry *dentry, bool is_dir)
|
||||
{
|
||||
int err;
|
||||
const struct cred *old_cred;
|
||||
bool lower_positive = ovl_lower_positive(dentry);
|
||||
LIST_HEAD(list);
|
||||
|
||||
@@ -940,12 +936,12 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
if (!lower_positive)
|
||||
err = ovl_remove_upper(dentry, is_dir, &list);
|
||||
else
|
||||
err = ovl_remove_and_whiteout(dentry, &list);
|
||||
ovl_revert_creds(old_cred);
|
||||
with_ovl_creds(dentry->d_sb) {
|
||||
if (!lower_positive)
|
||||
err = ovl_remove_upper(dentry, is_dir, &list);
|
||||
else
|
||||
err = ovl_remove_and_whiteout(dentry, &list);
|
||||
}
|
||||
if (!err) {
|
||||
if (is_dir)
|
||||
clear_nlink(dentry->d_inode);
|
||||
@@ -1109,102 +1105,108 @@ static int ovl_set_redirect(struct dentry *dentry, bool samedir)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
|
||||
struct dentry *old, struct inode *newdir,
|
||||
struct dentry *new, unsigned int flags)
|
||||
struct ovl_renamedata {
|
||||
struct renamedata;
|
||||
struct dentry *opaquedir;
|
||||
bool cleanup_whiteout;
|
||||
bool update_nlink;
|
||||
bool overwrite;
|
||||
};
|
||||
|
||||
static int ovl_rename_start(struct ovl_renamedata *ovlrd, struct list_head *list)
|
||||
{
|
||||
int err;
|
||||
struct dentry *old_upperdir;
|
||||
struct dentry *new_upperdir;
|
||||
struct renamedata rd = {};
|
||||
bool old_opaque;
|
||||
bool new_opaque;
|
||||
bool cleanup_whiteout = false;
|
||||
bool update_nlink = false;
|
||||
bool overwrite = !(flags & RENAME_EXCHANGE);
|
||||
struct dentry *old = ovlrd->old_dentry;
|
||||
struct dentry *new = ovlrd->new_dentry;
|
||||
bool is_dir = d_is_dir(old);
|
||||
bool new_is_dir = d_is_dir(new);
|
||||
bool samedir = olddir == newdir;
|
||||
struct dentry *opaquedir = NULL;
|
||||
struct dentry *whiteout = NULL;
|
||||
const struct cred *old_cred = NULL;
|
||||
struct ovl_fs *ofs = OVL_FS(old->d_sb);
|
||||
LIST_HEAD(list);
|
||||
int err;
|
||||
|
||||
err = -EINVAL;
|
||||
if (flags & ~(RENAME_EXCHANGE | RENAME_NOREPLACE))
|
||||
goto out;
|
||||
if (ovlrd->flags & ~(RENAME_EXCHANGE | RENAME_NOREPLACE))
|
||||
return -EINVAL;
|
||||
|
||||
flags &= ~RENAME_NOREPLACE;
|
||||
ovlrd->flags &= ~RENAME_NOREPLACE;
|
||||
|
||||
/* Don't copy up directory trees */
|
||||
err = -EXDEV;
|
||||
if (!ovl_can_move(old))
|
||||
goto out;
|
||||
if (!overwrite && !ovl_can_move(new))
|
||||
goto out;
|
||||
return err;
|
||||
if (!ovlrd->overwrite && !ovl_can_move(new))
|
||||
return err;
|
||||
|
||||
if (overwrite && new_is_dir && !ovl_pure_upper(new)) {
|
||||
err = ovl_check_empty_dir(new, &list);
|
||||
if (ovlrd->overwrite && new_is_dir && !ovl_pure_upper(new)) {
|
||||
err = ovl_check_empty_dir(new, list);
|
||||
if (err)
|
||||
goto out;
|
||||
return err;
|
||||
}
|
||||
|
||||
if (overwrite) {
|
||||
if (ovlrd->overwrite) {
|
||||
if (ovl_lower_positive(old)) {
|
||||
if (!ovl_dentry_is_whiteout(new)) {
|
||||
/* Whiteout source */
|
||||
flags |= RENAME_WHITEOUT;
|
||||
ovlrd->flags |= RENAME_WHITEOUT;
|
||||
} else {
|
||||
/* Switch whiteouts */
|
||||
flags |= RENAME_EXCHANGE;
|
||||
ovlrd->flags |= RENAME_EXCHANGE;
|
||||
}
|
||||
} else if (is_dir && ovl_dentry_is_whiteout(new)) {
|
||||
flags |= RENAME_EXCHANGE;
|
||||
cleanup_whiteout = true;
|
||||
ovlrd->flags |= RENAME_EXCHANGE;
|
||||
ovlrd->cleanup_whiteout = true;
|
||||
}
|
||||
}
|
||||
|
||||
err = ovl_copy_up(old);
|
||||
if (err)
|
||||
goto out;
|
||||
return err;
|
||||
|
||||
err = ovl_copy_up(new->d_parent);
|
||||
if (err)
|
||||
goto out;
|
||||
if (!overwrite) {
|
||||
return err;
|
||||
|
||||
if (!ovlrd->overwrite) {
|
||||
err = ovl_copy_up(new);
|
||||
if (err)
|
||||
goto out;
|
||||
return err;
|
||||
} else if (d_inode(new)) {
|
||||
err = ovl_nlink_start(new);
|
||||
if (err)
|
||||
goto out;
|
||||
return err;
|
||||
|
||||
update_nlink = true;
|
||||
ovlrd->update_nlink = true;
|
||||
}
|
||||
|
||||
if (!update_nlink) {
|
||||
if (!ovlrd->update_nlink) {
|
||||
/* ovl_nlink_start() took ovl_want_write() */
|
||||
err = ovl_want_write(old);
|
||||
if (err)
|
||||
goto out;
|
||||
return err;
|
||||
}
|
||||
|
||||
old_cred = ovl_override_creds(old->d_sb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!list_empty(&list)) {
|
||||
opaquedir = ovl_clear_empty(new, &list);
|
||||
err = PTR_ERR(opaquedir);
|
||||
if (IS_ERR(opaquedir)) {
|
||||
opaquedir = NULL;
|
||||
goto out_revert_creds;
|
||||
}
|
||||
static int ovl_rename_upper(struct ovl_renamedata *ovlrd, struct list_head *list)
|
||||
{
|
||||
struct dentry *old = ovlrd->old_dentry;
|
||||
struct dentry *new = ovlrd->new_dentry;
|
||||
struct ovl_fs *ofs = OVL_FS(old->d_sb);
|
||||
struct dentry *old_upperdir = ovl_dentry_upper(old->d_parent);
|
||||
struct dentry *new_upperdir = ovl_dentry_upper(new->d_parent);
|
||||
bool is_dir = d_is_dir(old);
|
||||
bool new_is_dir = d_is_dir(new);
|
||||
bool samedir = old->d_parent == new->d_parent;
|
||||
struct renamedata rd = {};
|
||||
struct dentry *de;
|
||||
struct dentry *whiteout = NULL;
|
||||
bool old_opaque, new_opaque;
|
||||
int err;
|
||||
|
||||
if (!list_empty(list)) {
|
||||
de = ovl_clear_empty(new, list);
|
||||
if (IS_ERR(de))
|
||||
return PTR_ERR(de);
|
||||
ovlrd->opaquedir = de;
|
||||
}
|
||||
|
||||
old_upperdir = ovl_dentry_upper(old->d_parent);
|
||||
new_upperdir = ovl_dentry_upper(new->d_parent);
|
||||
|
||||
if (!samedir) {
|
||||
/*
|
||||
* When moving a merge dir or non-dir with copy up origin into
|
||||
@@ -1215,26 +1217,25 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
|
||||
if (ovl_type_origin(old)) {
|
||||
err = ovl_set_impure(new->d_parent, new_upperdir);
|
||||
if (err)
|
||||
goto out_revert_creds;
|
||||
return err;
|
||||
}
|
||||
if (!overwrite && ovl_type_origin(new)) {
|
||||
if (!ovlrd->overwrite && ovl_type_origin(new)) {
|
||||
err = ovl_set_impure(old->d_parent, old_upperdir);
|
||||
if (err)
|
||||
goto out_revert_creds;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
rd.mnt_idmap = ovl_upper_mnt_idmap(ofs);
|
||||
rd.old_parent = old_upperdir;
|
||||
rd.new_parent = new_upperdir;
|
||||
rd.flags = flags;
|
||||
rd.flags = ovlrd->flags;
|
||||
|
||||
err = start_renaming(&rd, 0,
|
||||
&QSTR_LEN(old->d_name.name, old->d_name.len),
|
||||
&QSTR_LEN(new->d_name.name, new->d_name.len));
|
||||
|
||||
if (err)
|
||||
goto out_revert_creds;
|
||||
return err;
|
||||
|
||||
err = -ESTALE;
|
||||
if (!ovl_matches_upper(old, rd.old_dentry))
|
||||
@@ -1245,8 +1246,8 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
|
||||
|
||||
err = -ESTALE;
|
||||
if (d_inode(new) && ovl_dentry_upper(new)) {
|
||||
if (opaquedir) {
|
||||
if (rd.new_dentry != opaquedir)
|
||||
if (ovlrd->opaquedir) {
|
||||
if (rd.new_dentry != ovlrd->opaquedir)
|
||||
goto out_unlock;
|
||||
} else {
|
||||
if (!ovl_matches_upper(new, rd.new_dentry))
|
||||
@@ -1257,7 +1258,7 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
|
||||
if (!new_opaque || !ovl_upper_is_whiteout(ofs, rd.new_dentry))
|
||||
goto out_unlock;
|
||||
} else {
|
||||
if (flags & RENAME_EXCHANGE)
|
||||
if (ovlrd->flags & RENAME_EXCHANGE)
|
||||
goto out_unlock;
|
||||
}
|
||||
}
|
||||
@@ -1273,9 +1274,9 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
|
||||
if (!overwrite && ovl_type_merge_or_lower(new))
|
||||
if (!ovlrd->overwrite && ovl_type_merge_or_lower(new))
|
||||
err = ovl_set_redirect(new, samedir);
|
||||
else if (!overwrite && new_is_dir && !new_opaque &&
|
||||
else if (!ovlrd->overwrite && new_is_dir && !new_opaque &&
|
||||
ovl_type_merge(old->d_parent))
|
||||
err = ovl_set_opaque_xerr(new, rd.new_dentry, -EXDEV);
|
||||
if (err)
|
||||
@@ -1283,20 +1284,21 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
|
||||
|
||||
err = ovl_do_rename_rd(&rd);
|
||||
|
||||
if (!err && cleanup_whiteout)
|
||||
if (!err && ovlrd->cleanup_whiteout)
|
||||
whiteout = dget(rd.new_dentry);
|
||||
|
||||
out_unlock:
|
||||
end_renaming(&rd);
|
||||
|
||||
if (err)
|
||||
goto out_revert_creds;
|
||||
return err;
|
||||
|
||||
if (whiteout) {
|
||||
ovl_cleanup(ofs, old_upperdir, whiteout);
|
||||
dput(whiteout);
|
||||
}
|
||||
|
||||
if (overwrite && d_inode(new)) {
|
||||
if (ovlrd->overwrite && d_inode(new)) {
|
||||
if (new_is_dir)
|
||||
clear_nlink(d_inode(new));
|
||||
else
|
||||
@@ -1304,7 +1306,7 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
|
||||
}
|
||||
|
||||
ovl_dir_modified(old->d_parent, ovl_type_origin(old) ||
|
||||
(!overwrite && ovl_type_origin(new)));
|
||||
(!ovlrd->overwrite && ovl_type_origin(new)));
|
||||
ovl_dir_modified(new->d_parent, ovl_type_origin(old) ||
|
||||
(d_inode(new) && ovl_type_origin(new)));
|
||||
|
||||
@@ -1313,26 +1315,47 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
|
||||
if (d_inode(new) && ovl_dentry_upper(new))
|
||||
ovl_copyattr(d_inode(new));
|
||||
|
||||
out_revert_creds:
|
||||
ovl_revert_creds(old_cred);
|
||||
if (update_nlink)
|
||||
ovl_nlink_end(new);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void ovl_rename_end(struct ovl_renamedata *ovlrd)
|
||||
{
|
||||
if (ovlrd->update_nlink)
|
||||
ovl_nlink_end(ovlrd->new_dentry);
|
||||
else
|
||||
ovl_drop_write(old);
|
||||
out:
|
||||
dput(opaquedir);
|
||||
ovl_drop_write(ovlrd->old_dentry);
|
||||
}
|
||||
|
||||
static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
|
||||
struct dentry *old, struct inode *newdir,
|
||||
struct dentry *new, unsigned int flags)
|
||||
{
|
||||
struct ovl_renamedata ovlrd = {
|
||||
.old_parent = old->d_parent,
|
||||
.old_dentry = old,
|
||||
.new_parent = new->d_parent,
|
||||
.new_dentry = new,
|
||||
.flags = flags,
|
||||
.overwrite = !(flags & RENAME_EXCHANGE),
|
||||
};
|
||||
LIST_HEAD(list);
|
||||
int err;
|
||||
|
||||
err = ovl_rename_start(&ovlrd, &list);
|
||||
if (!err) {
|
||||
with_ovl_creds(old->d_sb)
|
||||
err = ovl_rename_upper(&ovlrd, &list);
|
||||
ovl_rename_end(&ovlrd);
|
||||
}
|
||||
|
||||
dput(ovlrd.opaquedir);
|
||||
ovl_cache_free(&list);
|
||||
return err;
|
||||
|
||||
out_unlock:
|
||||
end_renaming(&rd);
|
||||
goto out_revert_creds;
|
||||
}
|
||||
|
||||
static int ovl_create_tmpfile(struct file *file, struct dentry *dentry,
|
||||
struct inode *inode, umode_t mode)
|
||||
{
|
||||
const struct cred *old_cred, *new_cred = NULL;
|
||||
struct path realparentpath;
|
||||
struct file *realfile;
|
||||
struct ovl_file *of;
|
||||
@@ -1341,41 +1364,36 @@ static int ovl_create_tmpfile(struct file *file, struct dentry *dentry,
|
||||
int flags = file->f_flags | OVL_OPEN_FLAGS;
|
||||
int err;
|
||||
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
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;
|
||||
}
|
||||
with_ovl_creds(dentry->d_sb) {
|
||||
scoped_class(ovl_override_creator_creds, cred, dentry, inode, mode) {
|
||||
if (IS_ERR(cred))
|
||||
return PTR_ERR(cred);
|
||||
|
||||
ovl_path_upper(dentry->d_parent, &realparentpath);
|
||||
realfile = backing_tmpfile_open(&file->f_path, flags, &realparentpath,
|
||||
mode, current_cred());
|
||||
err = PTR_ERR_OR_ZERO(realfile);
|
||||
pr_debug("tmpfile/open(%pd2, 0%o) = %i\n", realparentpath.dentry, mode, err);
|
||||
if (err)
|
||||
goto out_revert_creds;
|
||||
ovl_path_upper(dentry->d_parent, &realparentpath);
|
||||
realfile = backing_tmpfile_open(&file->f_path, flags, &realparentpath,
|
||||
mode, current_cred());
|
||||
err = PTR_ERR_OR_ZERO(realfile);
|
||||
pr_debug("tmpfile/open(%pd2, 0%o) = %i\n", realparentpath.dentry, mode, err);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
of = ovl_file_alloc(realfile);
|
||||
if (!of) {
|
||||
fput(realfile);
|
||||
err = -ENOMEM;
|
||||
goto out_revert_creds;
|
||||
}
|
||||
of = ovl_file_alloc(realfile);
|
||||
if (!of) {
|
||||
fput(realfile);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* 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 = of;
|
||||
} else {
|
||||
dput(newdentry);
|
||||
ovl_file_free(of);
|
||||
/* 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 = of;
|
||||
} else {
|
||||
dput(newdentry);
|
||||
ovl_file_free(of);
|
||||
}
|
||||
}
|
||||
}
|
||||
out_revert_creds:
|
||||
ovl_revert_creds(old_cred);
|
||||
put_cred(new_cred);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ static struct file *ovl_open_realfile(const struct file *file,
|
||||
struct inode *inode = file_inode(file);
|
||||
struct mnt_idmap *real_idmap;
|
||||
struct file *realfile;
|
||||
const struct cred *old_cred;
|
||||
int flags = file->f_flags | OVL_OPEN_FLAGS;
|
||||
int acc_mode = ACC_MODE(flags);
|
||||
int err;
|
||||
@@ -39,19 +38,19 @@ static struct file *ovl_open_realfile(const struct file *file,
|
||||
if (flags & O_APPEND)
|
||||
acc_mode |= MAY_APPEND;
|
||||
|
||||
old_cred = ovl_override_creds(inode->i_sb);
|
||||
real_idmap = mnt_idmap(realpath->mnt);
|
||||
err = inode_permission(real_idmap, realinode, MAY_OPEN | acc_mode);
|
||||
if (err) {
|
||||
realfile = ERR_PTR(err);
|
||||
} else {
|
||||
if (!inode_owner_or_capable(real_idmap, realinode))
|
||||
flags &= ~O_NOATIME;
|
||||
with_ovl_creds(inode->i_sb) {
|
||||
real_idmap = mnt_idmap(realpath->mnt);
|
||||
err = inode_permission(real_idmap, realinode, MAY_OPEN | acc_mode);
|
||||
if (err) {
|
||||
realfile = ERR_PTR(err);
|
||||
} else {
|
||||
if (!inode_owner_or_capable(real_idmap, realinode))
|
||||
flags &= ~O_NOATIME;
|
||||
|
||||
realfile = backing_file_open(file_user_path(file),
|
||||
flags, realpath, current_cred());
|
||||
realfile = backing_file_open(file_user_path(file),
|
||||
flags, realpath, current_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,
|
||||
@@ -244,7 +243,6 @@ static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct file *realfile;
|
||||
const struct cred *old_cred;
|
||||
loff_t ret;
|
||||
|
||||
/*
|
||||
@@ -273,9 +271,8 @@ static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
|
||||
ovl_inode_lock(inode);
|
||||
realfile->f_pos = file->f_pos;
|
||||
|
||||
old_cred = ovl_override_creds(inode->i_sb);
|
||||
ret = vfs_llseek(realfile, offset, whence);
|
||||
ovl_revert_creds(old_cred);
|
||||
with_ovl_creds(inode->i_sb)
|
||||
ret = vfs_llseek(realfile, offset, whence);
|
||||
|
||||
file->f_pos = realfile->f_pos;
|
||||
ovl_inode_unlock(inode);
|
||||
@@ -447,7 +444,6 @@ static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
|
||||
enum ovl_path_type type;
|
||||
struct path upperpath;
|
||||
struct file *upperfile;
|
||||
const struct cred *old_cred;
|
||||
int ret;
|
||||
|
||||
ret = ovl_sync_status(OVL_FS(file_inode(file)->i_sb));
|
||||
@@ -464,11 +460,8 @@ static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
|
||||
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;
|
||||
with_ovl_creds(file_inode(file)->i_sb)
|
||||
return vfs_fsync_range(upperfile, start, end, datasync);
|
||||
}
|
||||
|
||||
static int ovl_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
@@ -486,7 +479,6 @@ static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct file *realfile;
|
||||
const struct cred *old_cred;
|
||||
int ret;
|
||||
|
||||
inode_lock(inode);
|
||||
@@ -501,9 +493,8 @@ static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len
|
||||
if (IS_ERR(realfile))
|
||||
goto out_unlock;
|
||||
|
||||
old_cred = ovl_override_creds(file_inode(file)->i_sb);
|
||||
ret = vfs_fallocate(realfile, mode, offset, len);
|
||||
ovl_revert_creds(old_cred);
|
||||
with_ovl_creds(inode->i_sb)
|
||||
ret = vfs_fallocate(realfile, mode, offset, len);
|
||||
|
||||
/* Update size */
|
||||
ovl_file_modified(file);
|
||||
@@ -517,18 +508,13 @@ out_unlock:
|
||||
static int ovl_fadvise(struct file *file, loff_t offset, loff_t len, int advice)
|
||||
{
|
||||
struct file *realfile;
|
||||
const struct cred *old_cred;
|
||||
int 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(realfile, offset, len, advice);
|
||||
ovl_revert_creds(old_cred);
|
||||
|
||||
return ret;
|
||||
with_ovl_creds(file_inode(file)->i_sb)
|
||||
return vfs_fadvise(realfile, offset, len, advice);
|
||||
}
|
||||
|
||||
enum ovl_copyop {
|
||||
@@ -543,7 +529,6 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in,
|
||||
{
|
||||
struct inode *inode_out = file_inode(file_out);
|
||||
struct file *realfile_in, *realfile_out;
|
||||
const struct cred *old_cred;
|
||||
loff_t ret;
|
||||
|
||||
inode_lock(inode_out);
|
||||
@@ -565,25 +550,25 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_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(realfile_in, pos_in,
|
||||
realfile_out, pos_out, len, flags);
|
||||
break;
|
||||
with_ovl_creds(file_inode(file_out)->i_sb) {
|
||||
switch (op) {
|
||||
case OVL_COPY:
|
||||
ret = vfs_copy_file_range(realfile_in, pos_in,
|
||||
realfile_out, pos_out, len, flags);
|
||||
break;
|
||||
|
||||
case OVL_CLONE:
|
||||
ret = vfs_clone_file_range(realfile_in, pos_in,
|
||||
realfile_out, pos_out, len, flags);
|
||||
break;
|
||||
case OVL_CLONE:
|
||||
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(realfile_in, pos_in,
|
||||
realfile_out, pos_out, len,
|
||||
flags);
|
||||
break;
|
||||
case OVL_DEDUPE:
|
||||
ret = vfs_dedupe_file_range_one(realfile_in, pos_in,
|
||||
realfile_out, pos_out, len,
|
||||
flags);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ovl_revert_creds(old_cred);
|
||||
|
||||
/* Update size */
|
||||
ovl_file_modified(file_out);
|
||||
@@ -632,7 +617,6 @@ 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 file *realfile;
|
||||
const struct cred *old_cred;
|
||||
int err = 0;
|
||||
|
||||
realfile = ovl_real_file(file);
|
||||
@@ -640,9 +624,8 @@ static int ovl_flush(struct file *file, fl_owner_t id)
|
||||
return PTR_ERR(realfile);
|
||||
|
||||
if (realfile->f_op->flush) {
|
||||
old_cred = ovl_override_creds(file_inode(file)->i_sb);
|
||||
err = realfile->f_op->flush(realfile, id);
|
||||
ovl_revert_creds(old_cred);
|
||||
with_ovl_creds(file_inode(file)->i_sb)
|
||||
err = realfile->f_op->flush(realfile, id);
|
||||
}
|
||||
|
||||
return err;
|
||||
|
||||
@@ -25,7 +25,6 @@ int ovl_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
||||
bool full_copy_up = false;
|
||||
struct dentry *upperdentry;
|
||||
const struct cred *old_cred;
|
||||
|
||||
err = setattr_prepare(&nop_mnt_idmap, dentry, attr);
|
||||
if (err)
|
||||
@@ -78,9 +77,8 @@ int ovl_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
goto out_put_write;
|
||||
|
||||
inode_lock(upperdentry->d_inode);
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
err = ovl_do_notify_change(ofs, upperdentry, attr);
|
||||
ovl_revert_creds(old_cred);
|
||||
with_ovl_creds(dentry->d_sb)
|
||||
err = ovl_do_notify_change(ofs, upperdentry, attr);
|
||||
if (!err)
|
||||
ovl_copyattr(dentry->d_inode);
|
||||
inode_unlock(upperdentry->d_inode);
|
||||
@@ -153,13 +151,22 @@ static void ovl_map_dev_ino(struct dentry *dentry, struct kstat *stat, int fsid)
|
||||
}
|
||||
}
|
||||
|
||||
static inline int ovl_real_getattr_nosec(struct super_block *sb,
|
||||
const struct path *path,
|
||||
struct kstat *stat, u32 request_mask,
|
||||
unsigned int flags)
|
||||
{
|
||||
with_ovl_creds(sb)
|
||||
return vfs_getattr_nosec(path, stat, request_mask, flags);
|
||||
}
|
||||
|
||||
int ovl_getattr(struct mnt_idmap *idmap, const struct path *path,
|
||||
struct kstat *stat, u32 request_mask, unsigned int flags)
|
||||
{
|
||||
struct dentry *dentry = path->dentry;
|
||||
struct super_block *sb = dentry->d_sb;
|
||||
enum ovl_path_type type;
|
||||
struct path realpath;
|
||||
const struct cred *old_cred;
|
||||
struct inode *inode = d_inode(dentry);
|
||||
bool is_dir = S_ISDIR(inode->i_mode);
|
||||
int fsid = 0;
|
||||
@@ -169,10 +176,9 @@ int ovl_getattr(struct mnt_idmap *idmap, const struct path *path,
|
||||
metacopy_blocks = ovl_is_metacopy_dentry(dentry);
|
||||
|
||||
type = ovl_path_real(dentry, &realpath);
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
err = vfs_getattr_nosec(&realpath, stat, request_mask, flags);
|
||||
err = ovl_real_getattr_nosec(sb, &realpath, stat, request_mask, flags);
|
||||
if (err)
|
||||
goto out;
|
||||
return err;
|
||||
|
||||
/* Report the effective immutable/append-only STATX flags */
|
||||
generic_fill_statx_attr(inode, stat);
|
||||
@@ -195,10 +201,9 @@ int ovl_getattr(struct mnt_idmap *idmap, const struct path *path,
|
||||
(!is_dir ? STATX_NLINK : 0);
|
||||
|
||||
ovl_path_lower(dentry, &realpath);
|
||||
err = vfs_getattr_nosec(&realpath, &lowerstat, lowermask,
|
||||
flags);
|
||||
err = ovl_real_getattr_nosec(sb, &realpath, &lowerstat, lowermask, flags);
|
||||
if (err)
|
||||
goto out;
|
||||
return err;
|
||||
|
||||
/*
|
||||
* Lower hardlinks may be broken on copy up to different
|
||||
@@ -248,10 +253,10 @@ int ovl_getattr(struct mnt_idmap *idmap, const struct path *path,
|
||||
|
||||
ovl_path_lowerdata(dentry, &realpath);
|
||||
if (realpath.dentry) {
|
||||
err = vfs_getattr_nosec(&realpath, &lowerdatastat,
|
||||
lowermask, flags);
|
||||
err = ovl_real_getattr_nosec(sb, &realpath, &lowerdatastat,
|
||||
lowermask, flags);
|
||||
if (err)
|
||||
goto out;
|
||||
return err;
|
||||
} else {
|
||||
lowerdatastat.blocks =
|
||||
round_up(stat->size, stat->blksize) >> 9;
|
||||
@@ -279,9 +284,6 @@ int ovl_getattr(struct mnt_idmap *idmap, const struct path *path,
|
||||
if (!is_dir && ovl_test_flag(OVL_INDEX, d_inode(dentry)))
|
||||
stat->nlink = dentry->d_inode->i_nlink;
|
||||
|
||||
out:
|
||||
ovl_revert_creds(old_cred);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -291,7 +293,6 @@ int ovl_permission(struct mnt_idmap *idmap,
|
||||
struct inode *upperinode = ovl_inode_upper(inode);
|
||||
struct inode *realinode;
|
||||
struct path realpath;
|
||||
const struct cred *old_cred;
|
||||
int err;
|
||||
|
||||
/* Careful in RCU walk mode */
|
||||
@@ -309,33 +310,26 @@ int ovl_permission(struct mnt_idmap *idmap,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
old_cred = ovl_override_creds(inode->i_sb);
|
||||
if (!upperinode &&
|
||||
!special_file(realinode->i_mode) && mask & MAY_WRITE) {
|
||||
mask &= ~(MAY_WRITE | MAY_APPEND);
|
||||
/* Make sure mounter can read file for copy up later */
|
||||
mask |= MAY_READ;
|
||||
}
|
||||
err = inode_permission(mnt_idmap(realpath.mnt), realinode, mask);
|
||||
ovl_revert_creds(old_cred);
|
||||
|
||||
return err;
|
||||
with_ovl_creds(inode->i_sb)
|
||||
return inode_permission(mnt_idmap(realpath.mnt), realinode, mask);
|
||||
}
|
||||
|
||||
static const char *ovl_get_link(struct dentry *dentry,
|
||||
struct inode *inode,
|
||||
struct delayed_call *done)
|
||||
{
|
||||
const struct cred *old_cred;
|
||||
const char *p;
|
||||
|
||||
if (!dentry)
|
||||
return ERR_PTR(-ECHILD);
|
||||
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
p = vfs_get_link(ovl_dentry_real(dentry), done);
|
||||
ovl_revert_creds(old_cred);
|
||||
return p;
|
||||
with_ovl_creds(dentry->d_sb)
|
||||
return vfs_get_link(ovl_dentry_real(dentry), done);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FS_POSIX_ACL
|
||||
@@ -465,11 +459,8 @@ struct posix_acl *do_ovl_get_acl(struct mnt_idmap *idmap,
|
||||
|
||||
acl = get_cached_acl_rcu(realinode, type);
|
||||
} else {
|
||||
const struct cred *old_cred;
|
||||
|
||||
old_cred = ovl_override_creds(inode->i_sb);
|
||||
acl = ovl_get_acl_path(&realpath, posix_acl_xattr_name(type), noperm);
|
||||
ovl_revert_creds(old_cred);
|
||||
with_ovl_creds(inode->i_sb)
|
||||
acl = ovl_get_acl_path(&realpath, posix_acl_xattr_name(type), noperm);
|
||||
}
|
||||
|
||||
return acl;
|
||||
@@ -481,7 +472,6 @@ static int ovl_set_or_remove_acl(struct dentry *dentry, struct inode *inode,
|
||||
int err;
|
||||
struct path realpath;
|
||||
const char *acl_name;
|
||||
const struct cred *old_cred;
|
||||
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
||||
struct dentry *upperdentry = ovl_dentry_upper(dentry);
|
||||
struct dentry *realdentry = upperdentry ?: ovl_dentry_lower(dentry);
|
||||
@@ -495,10 +485,8 @@ static int ovl_set_or_remove_acl(struct dentry *dentry, struct inode *inode,
|
||||
struct posix_acl *real_acl;
|
||||
|
||||
ovl_path_lower(dentry, &realpath);
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
real_acl = vfs_get_acl(mnt_idmap(realpath.mnt), realdentry,
|
||||
acl_name);
|
||||
ovl_revert_creds(old_cred);
|
||||
with_ovl_creds(dentry->d_sb)
|
||||
real_acl = vfs_get_acl(mnt_idmap(realpath.mnt), realdentry, acl_name);
|
||||
if (IS_ERR(real_acl)) {
|
||||
err = PTR_ERR(real_acl);
|
||||
goto out;
|
||||
@@ -518,12 +506,12 @@ static int ovl_set_or_remove_acl(struct dentry *dentry, struct inode *inode,
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
if (acl)
|
||||
err = ovl_do_set_acl(ofs, realdentry, acl_name, acl);
|
||||
else
|
||||
err = ovl_do_remove_acl(ofs, realdentry, acl_name);
|
||||
ovl_revert_creds(old_cred);
|
||||
with_ovl_creds(dentry->d_sb) {
|
||||
if (acl)
|
||||
err = ovl_do_set_acl(ofs, realdentry, acl_name, acl);
|
||||
else
|
||||
err = ovl_do_remove_acl(ofs, realdentry, acl_name);
|
||||
}
|
||||
ovl_drop_write(dentry);
|
||||
|
||||
/* copy c/mtime */
|
||||
@@ -588,9 +576,7 @@ int ovl_update_time(struct inode *inode, int flags)
|
||||
static int ovl_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||
u64 start, u64 len)
|
||||
{
|
||||
int err;
|
||||
struct inode *realinode = ovl_inode_realdata(inode);
|
||||
const struct cred *old_cred;
|
||||
|
||||
if (!realinode)
|
||||
return -EIO;
|
||||
@@ -598,11 +584,8 @@ static int ovl_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||
if (!realinode->i_op->fiemap)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
old_cred = ovl_override_creds(inode->i_sb);
|
||||
err = realinode->i_op->fiemap(realinode, fieinfo, start, len);
|
||||
ovl_revert_creds(old_cred);
|
||||
|
||||
return err;
|
||||
with_ovl_creds(inode->i_sb)
|
||||
return realinode->i_op->fiemap(realinode, fieinfo, start, len);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -653,7 +636,6 @@ int ovl_fileattr_set(struct mnt_idmap *idmap,
|
||||
{
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct path upperpath;
|
||||
const struct cred *old_cred;
|
||||
unsigned int flags;
|
||||
int err;
|
||||
|
||||
@@ -665,18 +647,18 @@ int ovl_fileattr_set(struct mnt_idmap *idmap,
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
old_cred = ovl_override_creds(inode->i_sb);
|
||||
/*
|
||||
* Store immutable/append-only flags in xattr and clear them
|
||||
* in upper fileattr (in case they were set by older kernel)
|
||||
* so children of "ovl-immutable" directories lower aliases of
|
||||
* "ovl-immutable" hardlinks could be copied up.
|
||||
* Clear xattr when flags are cleared.
|
||||
*/
|
||||
err = ovl_set_protattr(inode, upperpath.dentry, fa);
|
||||
if (!err)
|
||||
err = ovl_real_fileattr_set(&upperpath, fa);
|
||||
ovl_revert_creds(old_cred);
|
||||
with_ovl_creds(inode->i_sb) {
|
||||
/*
|
||||
* Store immutable/append-only flags in xattr and clear them
|
||||
* in upper fileattr (in case they were set by older kernel)
|
||||
* so children of "ovl-immutable" directories lower aliases of
|
||||
* "ovl-immutable" hardlinks could be copied up.
|
||||
* Clear xattr when flags are cleared.
|
||||
*/
|
||||
err = ovl_set_protattr(inode, upperpath.dentry, fa);
|
||||
if (!err)
|
||||
err = ovl_real_fileattr_set(&upperpath, fa);
|
||||
}
|
||||
ovl_drop_write(dentry);
|
||||
|
||||
/*
|
||||
@@ -730,15 +712,13 @@ int ovl_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
|
||||
{
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct path realpath;
|
||||
const struct cred *old_cred;
|
||||
int err;
|
||||
|
||||
ovl_path_real(dentry, &realpath);
|
||||
|
||||
old_cred = ovl_override_creds(inode->i_sb);
|
||||
err = ovl_real_fileattr_get(&realpath, fa);
|
||||
with_ovl_creds(inode->i_sb)
|
||||
err = ovl_real_fileattr_get(&realpath, fa);
|
||||
ovl_fileattr_prot_flags(inode, fa);
|
||||
ovl_revert_creds(old_cred);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -979,15 +979,10 @@ static int ovl_maybe_validate_verity(struct dentry *dentry)
|
||||
return err;
|
||||
|
||||
if (!ovl_test_flag(OVL_VERIFIED_DIGEST, inode)) {
|
||||
const struct cred *old_cred;
|
||||
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
|
||||
err = ovl_validate_verity(ofs, &metapath, &datapath);
|
||||
with_ovl_creds(dentry->d_sb)
|
||||
err = ovl_validate_verity(ofs, &metapath, &datapath);
|
||||
if (err == 0)
|
||||
ovl_set_flag(OVL_VERIFIED_DIGEST, inode);
|
||||
|
||||
ovl_revert_creds(old_cred);
|
||||
}
|
||||
|
||||
ovl_inode_unlock(inode);
|
||||
@@ -1001,7 +996,6 @@ static int ovl_maybe_lookup_lowerdata(struct dentry *dentry)
|
||||
struct inode *inode = d_inode(dentry);
|
||||
const char *redirect = ovl_lowerdata_redirect(inode);
|
||||
struct ovl_path datapath = {};
|
||||
const struct cred *old_cred;
|
||||
int err;
|
||||
|
||||
if (!redirect || ovl_dentry_lowerdata(dentry))
|
||||
@@ -1019,9 +1013,8 @@ static int ovl_maybe_lookup_lowerdata(struct dentry *dentry)
|
||||
if (ovl_dentry_lowerdata(dentry))
|
||||
goto out;
|
||||
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
err = ovl_lookup_data_layers(dentry, redirect, &datapath);
|
||||
ovl_revert_creds(old_cred);
|
||||
with_ovl_creds(dentry->d_sb)
|
||||
err = ovl_lookup_data_layers(dentry, redirect, &datapath);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
@@ -1077,57 +1070,44 @@ static bool ovl_check_follow_redirect(struct ovl_lookup_data *d)
|
||||
return true;
|
||||
}
|
||||
|
||||
struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
||||
unsigned int flags)
|
||||
struct ovl_lookup_ctx {
|
||||
struct dentry *dentry;
|
||||
struct ovl_entry *oe;
|
||||
struct ovl_path *stack;
|
||||
struct ovl_path *origin_path;
|
||||
struct dentry *upperdentry;
|
||||
struct dentry *index;
|
||||
struct inode *inode;
|
||||
unsigned int ctr;
|
||||
};
|
||||
|
||||
static int ovl_lookup_layers(struct ovl_lookup_ctx *ctx, struct ovl_lookup_data *d)
|
||||
{
|
||||
struct ovl_entry *oe = NULL;
|
||||
const struct cred *old_cred;
|
||||
struct dentry *dentry = ctx->dentry;
|
||||
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
||||
struct ovl_entry *poe = OVL_E(dentry->d_parent);
|
||||
struct ovl_entry *roe = OVL_E(dentry->d_sb->s_root);
|
||||
struct ovl_path *stack = NULL, *origin_path = NULL;
|
||||
struct dentry *upperdir, *upperdentry = NULL;
|
||||
struct dentry *origin = NULL;
|
||||
struct dentry *index = NULL;
|
||||
unsigned int ctr = 0;
|
||||
struct inode *inode = NULL;
|
||||
bool upperopaque = false;
|
||||
bool check_redirect = (ovl_redirect_follow(ofs) || ofs->numdatalayer);
|
||||
struct dentry *upperdir;
|
||||
struct dentry *this;
|
||||
unsigned int i;
|
||||
int err;
|
||||
struct dentry *origin = NULL;
|
||||
bool upperopaque = false;
|
||||
bool uppermetacopy = false;
|
||||
int metacopy_size = 0;
|
||||
struct ovl_lookup_data d = {
|
||||
.sb = dentry->d_sb,
|
||||
.dentry = dentry,
|
||||
.name = dentry->d_name,
|
||||
.is_dir = false,
|
||||
.opaque = false,
|
||||
.stop = false,
|
||||
.last = check_redirect ? false : !ovl_numlower(poe),
|
||||
.redirect = NULL,
|
||||
.upperredirect = NULL,
|
||||
.metacopy = 0,
|
||||
};
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
if (dentry->d_name.len > ofs->namelen)
|
||||
return ERR_PTR(-ENAMETOOLONG);
|
||||
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
upperdir = ovl_dentry_upper(dentry->d_parent);
|
||||
if (upperdir) {
|
||||
d.layer = &ofs->layers[0];
|
||||
err = ovl_lookup_layer(upperdir, &d, &upperdentry, true);
|
||||
d->layer = &ofs->layers[0];
|
||||
err = ovl_lookup_layer(upperdir, d, &ctx->upperdentry, true);
|
||||
if (err)
|
||||
goto out;
|
||||
return err;
|
||||
|
||||
if (upperdentry && upperdentry->d_flags & DCACHE_OP_REAL) {
|
||||
dput(upperdentry);
|
||||
err = -EREMOTE;
|
||||
goto out;
|
||||
}
|
||||
if (upperdentry && !d.is_dir) {
|
||||
if (ctx->upperdentry && ctx->upperdentry->d_flags & DCACHE_OP_REAL)
|
||||
return -EREMOTE;
|
||||
|
||||
if (ctx->upperdentry && !d->is_dir) {
|
||||
/*
|
||||
* Lookup copy up origin by decoding origin file handle.
|
||||
* We may get a disconnected dentry, which is fine,
|
||||
@@ -1138,50 +1118,50 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
||||
* number - it's the same as if we held a reference
|
||||
* to a dentry in lower layer that was moved under us.
|
||||
*/
|
||||
err = ovl_check_origin(ofs, upperdentry, &origin_path);
|
||||
err = ovl_check_origin(ofs, ctx->upperdentry, &ctx->origin_path);
|
||||
if (err)
|
||||
goto out_put_upper;
|
||||
return err;
|
||||
|
||||
if (d.metacopy)
|
||||
if (d->metacopy)
|
||||
uppermetacopy = true;
|
||||
metacopy_size = d.metacopy;
|
||||
metacopy_size = d->metacopy;
|
||||
}
|
||||
|
||||
if (d.redirect) {
|
||||
if (d->redirect) {
|
||||
err = -ENOMEM;
|
||||
d.upperredirect = kstrdup(d.redirect, GFP_KERNEL);
|
||||
if (!d.upperredirect)
|
||||
goto out_put_upper;
|
||||
if (d.redirect[0] == '/')
|
||||
d->upperredirect = kstrdup(d->redirect, GFP_KERNEL);
|
||||
if (!d->upperredirect)
|
||||
return err;
|
||||
if (d->redirect[0] == '/')
|
||||
poe = roe;
|
||||
}
|
||||
upperopaque = d.opaque;
|
||||
upperopaque = d->opaque;
|
||||
}
|
||||
|
||||
if (!d.stop && ovl_numlower(poe)) {
|
||||
if (!d->stop && ovl_numlower(poe)) {
|
||||
err = -ENOMEM;
|
||||
stack = ovl_stack_alloc(ofs->numlayer - 1);
|
||||
if (!stack)
|
||||
goto out_put_upper;
|
||||
ctx->stack = ovl_stack_alloc(ofs->numlayer - 1);
|
||||
if (!ctx->stack)
|
||||
return err;
|
||||
}
|
||||
|
||||
for (i = 0; !d.stop && i < ovl_numlower(poe); i++) {
|
||||
for (i = 0; !d->stop && i < ovl_numlower(poe); i++) {
|
||||
struct ovl_path lower = ovl_lowerstack(poe)[i];
|
||||
|
||||
if (!ovl_check_follow_redirect(&d)) {
|
||||
if (!ovl_check_follow_redirect(d)) {
|
||||
err = -EPERM;
|
||||
goto out_put;
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!check_redirect)
|
||||
d.last = i == ovl_numlower(poe) - 1;
|
||||
else if (d.is_dir || !ofs->numdatalayer)
|
||||
d.last = lower.layer->idx == ovl_numlower(roe);
|
||||
d->last = i == ovl_numlower(poe) - 1;
|
||||
else if (d->is_dir || !ofs->numdatalayer)
|
||||
d->last = lower.layer->idx == ovl_numlower(roe);
|
||||
|
||||
d.layer = lower.layer;
|
||||
err = ovl_lookup_layer(lower.dentry, &d, &this, false);
|
||||
d->layer = lower.layer;
|
||||
err = ovl_lookup_layer(lower.dentry, d, &this, false);
|
||||
if (err)
|
||||
goto out_put;
|
||||
return err;
|
||||
|
||||
if (!this)
|
||||
continue;
|
||||
@@ -1190,11 +1170,11 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
||||
* If no origin fh is stored in upper of a merge dir, store fh
|
||||
* of lower dir and set upper parent "impure".
|
||||
*/
|
||||
if (upperdentry && !ctr && !ofs->noxattr && d.is_dir) {
|
||||
err = ovl_fix_origin(ofs, dentry, this, upperdentry);
|
||||
if (ctx->upperdentry && !ctx->ctr && !ofs->noxattr && d->is_dir) {
|
||||
err = ovl_fix_origin(ofs, dentry, this, ctx->upperdentry);
|
||||
if (err) {
|
||||
dput(this);
|
||||
goto out_put;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1207,23 +1187,23 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
||||
* matches the dentry found using path based lookup,
|
||||
* otherwise error out.
|
||||
*/
|
||||
if (upperdentry && !ctr &&
|
||||
((d.is_dir && ovl_verify_lower(dentry->d_sb)) ||
|
||||
(!d.is_dir && ofs->config.index && origin_path))) {
|
||||
err = ovl_verify_origin(ofs, upperdentry, this, false);
|
||||
if (ctx->upperdentry && !ctx->ctr &&
|
||||
((d->is_dir && ovl_verify_lower(dentry->d_sb)) ||
|
||||
(!d->is_dir && ofs->config.index && ctx->origin_path))) {
|
||||
err = ovl_verify_origin(ofs, ctx->upperdentry, this, false);
|
||||
if (err) {
|
||||
dput(this);
|
||||
if (d.is_dir)
|
||||
if (d->is_dir)
|
||||
break;
|
||||
goto out_put;
|
||||
return err;
|
||||
}
|
||||
origin = this;
|
||||
}
|
||||
|
||||
if (!upperdentry && !d.is_dir && !ctr && d.metacopy)
|
||||
metacopy_size = d.metacopy;
|
||||
if (!ctx->upperdentry && !d->is_dir && !ctx->ctr && d->metacopy)
|
||||
metacopy_size = d->metacopy;
|
||||
|
||||
if (d.metacopy && ctr) {
|
||||
if (d->metacopy && ctx->ctr) {
|
||||
/*
|
||||
* Do not store intermediate metacopy dentries in
|
||||
* lower chain, except top most lower metacopy dentry.
|
||||
@@ -1233,15 +1213,15 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
||||
dput(this);
|
||||
this = NULL;
|
||||
} else {
|
||||
stack[ctr].dentry = this;
|
||||
stack[ctr].layer = lower.layer;
|
||||
ctr++;
|
||||
ctx->stack[ctx->ctr].dentry = this;
|
||||
ctx->stack[ctx->ctr].layer = lower.layer;
|
||||
ctx->ctr++;
|
||||
}
|
||||
|
||||
if (d.stop)
|
||||
if (d->stop)
|
||||
break;
|
||||
|
||||
if (d.redirect && d.redirect[0] == '/' && poe != roe) {
|
||||
if (d->redirect && d->redirect[0] == '/' && poe != roe) {
|
||||
poe = roe;
|
||||
/* Find the current layer on the root dentry */
|
||||
i = lower.layer->idx - 1;
|
||||
@@ -1252,12 +1232,12 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
||||
* Defer lookup of lowerdata in data-only layers to first access.
|
||||
* Don't require redirect=follow and metacopy=on in this case.
|
||||
*/
|
||||
if (d.metacopy && ctr && ofs->numdatalayer && d.absolute_redirect) {
|
||||
d.metacopy = 0;
|
||||
ctr++;
|
||||
} else if (!ovl_check_follow_redirect(&d)) {
|
||||
if (d->metacopy && ctx->ctr && ofs->numdatalayer && d->absolute_redirect) {
|
||||
d->metacopy = 0;
|
||||
ctx->ctr++;
|
||||
} else if (!ovl_check_follow_redirect(d)) {
|
||||
err = -EPERM;
|
||||
goto out_put;
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1268,20 +1248,20 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
||||
* For metacopy dentry, path based lookup will find lower dentries.
|
||||
* Just make sure a corresponding data dentry has been found.
|
||||
*/
|
||||
if (d.metacopy || (uppermetacopy && !ctr)) {
|
||||
if (d->metacopy || (uppermetacopy && !ctx->ctr)) {
|
||||
pr_warn_ratelimited("metacopy with no lower data found - abort lookup (%pd2)\n",
|
||||
dentry);
|
||||
err = -EIO;
|
||||
goto out_put;
|
||||
} else if (!d.is_dir && upperdentry && !ctr && origin_path) {
|
||||
if (WARN_ON(stack != NULL)) {
|
||||
return err;
|
||||
} else if (!d->is_dir && ctx->upperdentry && !ctx->ctr && ctx->origin_path) {
|
||||
if (WARN_ON(ctx->stack != NULL)) {
|
||||
err = -EIO;
|
||||
goto out_put;
|
||||
return err;
|
||||
}
|
||||
stack = origin_path;
|
||||
ctr = 1;
|
||||
origin = origin_path->dentry;
|
||||
origin_path = NULL;
|
||||
ctx->stack = ctx->origin_path;
|
||||
ctx->ctr = 1;
|
||||
origin = ctx->origin_path->dentry;
|
||||
ctx->origin_path = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1303,38 +1283,39 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
||||
* is enabled and if upper had an ORIGIN xattr.
|
||||
*
|
||||
*/
|
||||
if (!upperdentry && ctr)
|
||||
origin = stack[0].dentry;
|
||||
if (!ctx->upperdentry && ctx->ctr)
|
||||
origin = ctx->stack[0].dentry;
|
||||
|
||||
if (origin && ovl_indexdir(dentry->d_sb) &&
|
||||
(!d.is_dir || ovl_index_all(dentry->d_sb))) {
|
||||
index = ovl_lookup_index(ofs, upperdentry, origin, true);
|
||||
if (IS_ERR(index)) {
|
||||
err = PTR_ERR(index);
|
||||
index = NULL;
|
||||
goto out_put;
|
||||
(!d->is_dir || ovl_index_all(dentry->d_sb))) {
|
||||
ctx->index = ovl_lookup_index(ofs, ctx->upperdentry, origin, true);
|
||||
if (IS_ERR(ctx->index)) {
|
||||
err = PTR_ERR(ctx->index);
|
||||
ctx->index = NULL;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctr) {
|
||||
oe = ovl_alloc_entry(ctr);
|
||||
if (ctx->ctr) {
|
||||
ctx->oe = ovl_alloc_entry(ctx->ctr);
|
||||
err = -ENOMEM;
|
||||
if (!oe)
|
||||
goto out_put;
|
||||
if (!ctx->oe)
|
||||
return err;
|
||||
|
||||
ovl_stack_cpy(ovl_lowerstack(oe), stack, ctr);
|
||||
ovl_stack_cpy(ovl_lowerstack(ctx->oe), ctx->stack, ctx->ctr);
|
||||
}
|
||||
|
||||
if (upperopaque)
|
||||
ovl_dentry_set_opaque(dentry);
|
||||
if (d.xwhiteouts)
|
||||
if (d->xwhiteouts)
|
||||
ovl_dentry_set_xwhiteouts(dentry);
|
||||
|
||||
if (upperdentry)
|
||||
if (ctx->upperdentry)
|
||||
ovl_dentry_set_upper_alias(dentry);
|
||||
else if (index) {
|
||||
else if (ctx->index) {
|
||||
char *upperredirect;
|
||||
struct path upperpath = {
|
||||
.dentry = upperdentry = dget(index),
|
||||
.dentry = ctx->upperdentry = dget(ctx->index),
|
||||
.mnt = ovl_upper_mnt(ofs),
|
||||
};
|
||||
|
||||
@@ -1343,84 +1324,100 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
||||
* assignment happens only if upperdentry is non-NULL, and
|
||||
* this one only if upperdentry is NULL.
|
||||
*/
|
||||
d.upperredirect = ovl_get_redirect_xattr(ofs, &upperpath, 0);
|
||||
if (IS_ERR(d.upperredirect)) {
|
||||
err = PTR_ERR(d.upperredirect);
|
||||
d.upperredirect = NULL;
|
||||
goto out_free_oe;
|
||||
}
|
||||
upperredirect = ovl_get_redirect_xattr(ofs, &upperpath, 0);
|
||||
if (IS_ERR(upperredirect))
|
||||
return PTR_ERR(upperredirect);
|
||||
d->upperredirect = upperredirect;
|
||||
|
||||
err = ovl_check_metacopy_xattr(ofs, &upperpath, NULL);
|
||||
if (err < 0)
|
||||
goto out_free_oe;
|
||||
d.metacopy = uppermetacopy = err;
|
||||
return err;
|
||||
d->metacopy = uppermetacopy = err;
|
||||
metacopy_size = err;
|
||||
|
||||
if (!ovl_check_follow_redirect(&d)) {
|
||||
if (!ovl_check_follow_redirect(d)) {
|
||||
err = -EPERM;
|
||||
goto out_free_oe;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (upperdentry || ctr) {
|
||||
if (ctx->upperdentry || ctx->ctr) {
|
||||
struct inode *inode;
|
||||
struct ovl_inode_params oip = {
|
||||
.upperdentry = upperdentry,
|
||||
.oe = oe,
|
||||
.index = index,
|
||||
.redirect = d.upperredirect,
|
||||
.upperdentry = ctx->upperdentry,
|
||||
.oe = ctx->oe,
|
||||
.index = ctx->index,
|
||||
.redirect = d->upperredirect,
|
||||
};
|
||||
|
||||
/* Store lowerdata redirect for lazy lookup */
|
||||
if (ctr > 1 && !d.is_dir && !stack[ctr - 1].dentry) {
|
||||
oip.lowerdata_redirect = d.redirect;
|
||||
d.redirect = NULL;
|
||||
if (ctx->ctr > 1 && !d->is_dir && !ctx->stack[ctx->ctr - 1].dentry) {
|
||||
oip.lowerdata_redirect = d->redirect;
|
||||
d->redirect = NULL;
|
||||
}
|
||||
|
||||
inode = ovl_get_inode(dentry->d_sb, &oip);
|
||||
err = PTR_ERR(inode);
|
||||
if (IS_ERR(inode))
|
||||
goto out_free_oe;
|
||||
if (upperdentry && !uppermetacopy)
|
||||
ovl_set_flag(OVL_UPPERDATA, inode);
|
||||
return PTR_ERR(inode);
|
||||
|
||||
ctx->inode = inode;
|
||||
if (ctx->upperdentry && !uppermetacopy)
|
||||
ovl_set_flag(OVL_UPPERDATA, ctx->inode);
|
||||
|
||||
if (metacopy_size > OVL_METACOPY_MIN_SIZE)
|
||||
ovl_set_flag(OVL_HAS_DIGEST, inode);
|
||||
ovl_set_flag(OVL_HAS_DIGEST, ctx->inode);
|
||||
}
|
||||
|
||||
ovl_dentry_init_reval(dentry, upperdentry, OVL_I_E(inode));
|
||||
ovl_dentry_init_reval(dentry, ctx->upperdentry, OVL_I_E(ctx->inode));
|
||||
|
||||
ovl_revert_creds(old_cred);
|
||||
if (origin_path) {
|
||||
dput(origin_path->dentry);
|
||||
kfree(origin_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
||||
struct ovl_entry *poe = OVL_E(dentry->d_parent);
|
||||
bool check_redirect = (ovl_redirect_follow(ofs) || ofs->numdatalayer);
|
||||
int err;
|
||||
struct ovl_lookup_ctx ctx = {
|
||||
.dentry = dentry,
|
||||
};
|
||||
struct ovl_lookup_data d = {
|
||||
.sb = dentry->d_sb,
|
||||
.dentry = dentry,
|
||||
.name = dentry->d_name,
|
||||
.last = check_redirect ? false : !ovl_numlower(poe),
|
||||
};
|
||||
|
||||
if (dentry->d_name.len > ofs->namelen)
|
||||
return ERR_PTR(-ENAMETOOLONG);
|
||||
|
||||
with_ovl_creds(dentry->d_sb)
|
||||
err = ovl_lookup_layers(&ctx, &d);
|
||||
|
||||
if (ctx.origin_path) {
|
||||
dput(ctx.origin_path->dentry);
|
||||
kfree(ctx.origin_path);
|
||||
}
|
||||
dput(index);
|
||||
ovl_stack_free(stack, ctr);
|
||||
dput(ctx.index);
|
||||
ovl_stack_free(ctx.stack, ctx.ctr);
|
||||
kfree(d.redirect);
|
||||
return d_splice_alias(inode, dentry);
|
||||
|
||||
out_free_oe:
|
||||
ovl_free_entry(oe);
|
||||
out_put:
|
||||
dput(index);
|
||||
ovl_stack_free(stack, ctr);
|
||||
out_put_upper:
|
||||
if (origin_path) {
|
||||
dput(origin_path->dentry);
|
||||
kfree(origin_path);
|
||||
if (err) {
|
||||
ovl_free_entry(ctx.oe);
|
||||
dput(ctx.upperdentry);
|
||||
kfree(d.upperredirect);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
dput(upperdentry);
|
||||
kfree(d.upperredirect);
|
||||
out:
|
||||
kfree(d.redirect);
|
||||
ovl_revert_creds(old_cred);
|
||||
return ERR_PTR(err);
|
||||
|
||||
return d_splice_alias(ctx.inode, dentry);
|
||||
}
|
||||
|
||||
bool ovl_lower_positive(struct dentry *dentry)
|
||||
{
|
||||
struct ovl_entry *poe = OVL_E(dentry->d_parent);
|
||||
const struct qstr *name = &dentry->d_name;
|
||||
const struct cred *old_cred;
|
||||
unsigned int i;
|
||||
bool positive = false;
|
||||
bool done = false;
|
||||
@@ -1436,46 +1433,45 @@ bool ovl_lower_positive(struct dentry *dentry)
|
||||
if (!ovl_dentry_upper(dentry))
|
||||
return true;
|
||||
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
/* Positive upper -> have to look up lower to see whether it exists */
|
||||
for (i = 0; !done && !positive && i < ovl_numlower(poe); i++) {
|
||||
struct dentry *this;
|
||||
struct ovl_path *parentpath = &ovl_lowerstack(poe)[i];
|
||||
with_ovl_creds(dentry->d_sb) {
|
||||
/* Positive upper -> have to look up lower to see whether it exists */
|
||||
for (i = 0; !done && !positive && i < ovl_numlower(poe); i++) {
|
||||
struct dentry *this;
|
||||
struct ovl_path *parentpath = &ovl_lowerstack(poe)[i];
|
||||
|
||||
/*
|
||||
* We need to make a non-const copy of dentry->d_name,
|
||||
* because lookup_one_positive_unlocked() will hash name
|
||||
* with parentpath base, which is on another (lower fs).
|
||||
*/
|
||||
this = lookup_one_positive_unlocked(
|
||||
mnt_idmap(parentpath->layer->mnt),
|
||||
&QSTR_LEN(name->name, name->len),
|
||||
parentpath->dentry);
|
||||
if (IS_ERR(this)) {
|
||||
switch (PTR_ERR(this)) {
|
||||
case -ENOENT:
|
||||
case -ENAMETOOLONG:
|
||||
break;
|
||||
/*
|
||||
* We need to make a non-const copy of dentry->d_name,
|
||||
* because lookup_one_positive_unlocked() will hash name
|
||||
* with parentpath base, which is on another (lower fs).
|
||||
*/
|
||||
this = lookup_one_positive_unlocked(mnt_idmap(parentpath->layer->mnt),
|
||||
&QSTR_LEN(name->name, name->len),
|
||||
parentpath->dentry);
|
||||
if (IS_ERR(this)) {
|
||||
switch (PTR_ERR(this)) {
|
||||
case -ENOENT:
|
||||
case -ENAMETOOLONG:
|
||||
break;
|
||||
|
||||
default:
|
||||
/*
|
||||
* Assume something is there, we just couldn't
|
||||
* access it.
|
||||
*/
|
||||
positive = true;
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* Assume something is there, we just couldn't
|
||||
* access it.
|
||||
*/
|
||||
positive = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
struct path path = {
|
||||
.dentry = this,
|
||||
.mnt = parentpath->layer->mnt,
|
||||
};
|
||||
positive = !ovl_path_is_whiteout(OVL_FS(dentry->d_sb), &path);
|
||||
done = true;
|
||||
dput(this);
|
||||
}
|
||||
} else {
|
||||
struct path path = {
|
||||
.dentry = this,
|
||||
.mnt = parentpath->layer->mnt,
|
||||
};
|
||||
positive = !ovl_path_is_whiteout(OVL_FS(dentry->d_sb), &path);
|
||||
done = true;
|
||||
dput(this);
|
||||
}
|
||||
}
|
||||
ovl_revert_creds(old_cred);
|
||||
|
||||
return positive;
|
||||
}
|
||||
|
||||
@@ -455,7 +455,11 @@ 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);
|
||||
|
||||
EXTEND_CLASS(override_creds, _ovl, ovl_override_creds(sb), struct super_block *sb)
|
||||
|
||||
#define with_ovl_creds(sb) \
|
||||
scoped_class(override_creds_ovl, __UNIQUE_ID(label), sb)
|
||||
|
||||
static inline const struct cred *ovl_creds(struct super_block *sb)
|
||||
{
|
||||
|
||||
@@ -348,11 +348,7 @@ static bool ovl_fill_merge(struct dir_context *ctx, const char *name,
|
||||
|
||||
static int ovl_check_whiteouts(const struct path *path, struct ovl_readdir_data *rdd)
|
||||
{
|
||||
int err = 0;
|
||||
struct dentry *dentry, *dir = path->dentry;
|
||||
const struct cred *old_cred;
|
||||
|
||||
old_cred = ovl_override_creds(rdd->dentry->d_sb);
|
||||
|
||||
while (rdd->first_maybe_whiteout) {
|
||||
struct ovl_cache_entry *p =
|
||||
@@ -365,13 +361,11 @@ static int ovl_check_whiteouts(const struct path *path, struct ovl_readdir_data
|
||||
p->is_whiteout = ovl_is_whiteout(dentry);
|
||||
dput(dentry);
|
||||
} else if (PTR_ERR(dentry) == -EINTR) {
|
||||
err = -EINTR;
|
||||
break;
|
||||
return -EINTR;
|
||||
}
|
||||
}
|
||||
ovl_revert_creds(old_cred);
|
||||
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int ovl_dir_read(const struct path *realpath,
|
||||
@@ -838,36 +832,12 @@ static int ovl_iterate_real(struct file *file, struct dir_context *ctx)
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static int ovl_iterate(struct file *file, struct dir_context *ctx)
|
||||
static int ovl_iterate_merged(struct file *file, struct dir_context *ctx)
|
||||
{
|
||||
struct ovl_dir_file *od = file->private_data;
|
||||
struct dentry *dentry = file->f_path.dentry;
|
||||
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
||||
struct ovl_cache_entry *p;
|
||||
const struct cred *old_cred;
|
||||
int err;
|
||||
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
if (!ctx->pos)
|
||||
ovl_dir_reset(file);
|
||||
|
||||
if (od->is_real) {
|
||||
/*
|
||||
* If parent is merge, then need to adjust d_ino for '..', if
|
||||
* dir is impure then need to adjust d_ino for copied up
|
||||
* entries.
|
||||
*/
|
||||
if (ovl_xino_bits(ofs) ||
|
||||
(ovl_same_fs(ofs) &&
|
||||
(ovl_is_impure_dir(file) ||
|
||||
OVL_TYPE_MERGE(ovl_path_type(dentry->d_parent))))) {
|
||||
err = ovl_iterate_real(file, ctx);
|
||||
} else {
|
||||
err = iterate_dir(od->realfile, ctx);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
int err = 0;
|
||||
|
||||
if (!od->cache) {
|
||||
struct ovl_dir_cache *cache;
|
||||
@@ -875,7 +845,7 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx)
|
||||
cache = ovl_cache_get(dentry);
|
||||
err = PTR_ERR(cache);
|
||||
if (IS_ERR(cache))
|
||||
goto out;
|
||||
return err;
|
||||
|
||||
od->cache = cache;
|
||||
ovl_seek_cursor(od, ctx->pos);
|
||||
@@ -887,7 +857,7 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx)
|
||||
if (!p->ino || p->check_xwhiteout) {
|
||||
err = ovl_cache_update(&file->f_path, p, !p->ino);
|
||||
if (err)
|
||||
goto out;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
/* ovl_cache_update() sets is_whiteout on stale entry */
|
||||
@@ -898,12 +868,50 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx)
|
||||
od->cursor = p->l_node.next;
|
||||
ctx->pos++;
|
||||
}
|
||||
err = 0;
|
||||
out:
|
||||
ovl_revert_creds(old_cred);
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool ovl_need_adjust_d_ino(struct file *file)
|
||||
{
|
||||
struct dentry *dentry = file->f_path.dentry;
|
||||
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
|
||||
|
||||
/* If parent is merge, then need to adjust d_ino for '..' */
|
||||
if (ovl_xino_bits(ofs))
|
||||
return true;
|
||||
|
||||
/* Can't do consistent inode numbering */
|
||||
if (!ovl_same_fs(ofs))
|
||||
return false;
|
||||
|
||||
/* If dir is impure then need to adjust d_ino for copied up entries */
|
||||
if (ovl_is_impure_dir(file) ||
|
||||
OVL_TYPE_MERGE(ovl_path_type(dentry->d_parent)))
|
||||
return true;
|
||||
|
||||
/* Pure: no need to adjust d_ino */
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static int ovl_iterate(struct file *file, struct dir_context *ctx)
|
||||
{
|
||||
struct ovl_dir_file *od = file->private_data;
|
||||
|
||||
if (!ctx->pos)
|
||||
ovl_dir_reset(file);
|
||||
|
||||
with_ovl_creds(file_dentry(file)->d_sb) {
|
||||
if (!od->is_real)
|
||||
return ovl_iterate_merged(file, ctx);
|
||||
|
||||
if (ovl_need_adjust_d_ino(file))
|
||||
return ovl_iterate_real(file, ctx);
|
||||
|
||||
return iterate_dir(od->realfile, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static loff_t ovl_dir_llseek(struct file *file, loff_t offset, int origin)
|
||||
{
|
||||
loff_t res;
|
||||
@@ -947,14 +955,8 @@ out_unlock:
|
||||
static struct file *ovl_dir_open_realfile(const struct file *file,
|
||||
const struct path *realpath)
|
||||
{
|
||||
struct file *res;
|
||||
const struct cred *old_cred;
|
||||
|
||||
old_cred = ovl_override_creds(file_inode(file)->i_sb);
|
||||
res = ovl_path_open(realpath, O_RDONLY | (file->f_flags & O_LARGEFILE));
|
||||
ovl_revert_creds(old_cred);
|
||||
|
||||
return res;
|
||||
with_ovl_creds(file_inode(file)->i_sb)
|
||||
return ovl_path_open(realpath, O_RDONLY | (file->f_flags & O_LARGEFILE));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1075,11 +1077,9 @@ int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list)
|
||||
int err;
|
||||
struct ovl_cache_entry *p, *n;
|
||||
struct rb_root root = RB_ROOT;
|
||||
const struct cred *old_cred;
|
||||
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
err = ovl_dir_read_merged(dentry, list, &root);
|
||||
ovl_revert_creds(old_cred);
|
||||
with_ovl_creds(dentry->d_sb)
|
||||
err = ovl_dir_read_merged(dentry, list, &root);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
||||
@@ -1364,53 +1364,35 @@ static void ovl_set_d_op(struct super_block *sb)
|
||||
set_default_d_op(sb, &ovl_dentry_operations);
|
||||
}
|
||||
|
||||
int ovl_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
static int ovl_fill_super_creds(struct fs_context *fc, struct super_block *sb)
|
||||
{
|
||||
struct ovl_fs *ofs = sb->s_fs_info;
|
||||
struct cred *creator_cred = (struct cred *)ofs->creator_cred;
|
||||
struct ovl_fs_context *ctx = fc->fs_private;
|
||||
const struct cred *old_cred = NULL;
|
||||
struct dentry *root_dentry;
|
||||
struct ovl_entry *oe;
|
||||
struct ovl_layer *layers;
|
||||
struct cred *cred;
|
||||
struct ovl_entry *oe = NULL;
|
||||
int err;
|
||||
|
||||
err = -EIO;
|
||||
if (WARN_ON(fc->user_ns != current_user_ns()))
|
||||
goto out_err;
|
||||
|
||||
ovl_set_d_op(sb);
|
||||
|
||||
err = -ENOMEM;
|
||||
if (!ofs->creator_cred)
|
||||
ofs->creator_cred = cred = prepare_creds();
|
||||
else
|
||||
cred = (struct cred *)ofs->creator_cred;
|
||||
if (!cred)
|
||||
goto out_err;
|
||||
|
||||
old_cred = ovl_override_creds(sb);
|
||||
|
||||
err = ovl_fs_params_verify(ctx, &ofs->config);
|
||||
if (err)
|
||||
goto out_err;
|
||||
return err;
|
||||
|
||||
err = -EINVAL;
|
||||
if (ctx->nr == 0) {
|
||||
if (!(fc->sb_flags & SB_SILENT))
|
||||
pr_err("missing 'lowerdir'\n");
|
||||
goto out_err;
|
||||
return err;
|
||||
}
|
||||
|
||||
err = -ENOMEM;
|
||||
layers = kcalloc(ctx->nr + 1, sizeof(struct ovl_layer), GFP_KERNEL);
|
||||
if (!layers)
|
||||
goto out_err;
|
||||
return err;
|
||||
|
||||
ofs->config.lowerdirs = kcalloc(ctx->nr + 1, sizeof(char *), GFP_KERNEL);
|
||||
if (!ofs->config.lowerdirs) {
|
||||
kfree(layers);
|
||||
goto out_err;
|
||||
return err;
|
||||
}
|
||||
ofs->layers = layers;
|
||||
/*
|
||||
@@ -1443,12 +1425,12 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
err = -EINVAL;
|
||||
if (!ofs->config.workdir) {
|
||||
pr_err("missing 'workdir'\n");
|
||||
goto out_err;
|
||||
return err;
|
||||
}
|
||||
|
||||
err = ovl_get_upper(sb, ofs, &layers[0], &ctx->upper);
|
||||
if (err)
|
||||
goto out_err;
|
||||
return err;
|
||||
|
||||
upper_sb = ovl_upper_mnt(ofs)->mnt_sb;
|
||||
if (!ovl_should_sync(ofs)) {
|
||||
@@ -1456,13 +1438,13 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
if (errseq_check(&upper_sb->s_wb_err, ofs->errseq)) {
|
||||
err = -EIO;
|
||||
pr_err("Cannot mount volatile when upperdir has an unseen error. Sync upperdir fs to clear state.\n");
|
||||
goto out_err;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
err = ovl_get_workdir(sb, ofs, &ctx->upper, &ctx->work);
|
||||
if (err)
|
||||
goto out_err;
|
||||
return err;
|
||||
|
||||
if (!ofs->workdir)
|
||||
sb->s_flags |= SB_RDONLY;
|
||||
@@ -1473,7 +1455,7 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
oe = ovl_get_lowerstack(sb, ctx, ofs, layers);
|
||||
err = PTR_ERR(oe);
|
||||
if (IS_ERR(oe))
|
||||
goto out_err;
|
||||
return err;
|
||||
|
||||
/* If the upper fs is nonexistent, we mark overlayfs r/o too */
|
||||
if (!ovl_upper_mnt(ofs))
|
||||
@@ -1526,7 +1508,7 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
sb->s_export_op = &ovl_export_fid_operations;
|
||||
|
||||
/* Never override disk quota limits or use reserved space */
|
||||
cap_lower(cred->cap_effective, CAP_SYS_RESOURCE);
|
||||
cap_lower(creator_cred->cap_effective, CAP_SYS_RESOURCE);
|
||||
|
||||
sb->s_magic = OVERLAYFS_SUPER_MAGIC;
|
||||
sb->s_xattr = ovl_xattr_handlers(ofs);
|
||||
@@ -1544,27 +1526,44 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
sb->s_iflags |= SB_I_EVM_HMAC_UNSUPPORTED;
|
||||
|
||||
err = -ENOMEM;
|
||||
root_dentry = ovl_get_root(sb, ctx->upper.dentry, oe);
|
||||
if (!root_dentry)
|
||||
sb->s_root = ovl_get_root(sb, ctx->upper.dentry, oe);
|
||||
if (!sb->s_root)
|
||||
goto out_free_oe;
|
||||
|
||||
sb->s_root = root_dentry;
|
||||
|
||||
ovl_revert_creds(old_cred);
|
||||
return 0;
|
||||
|
||||
out_free_oe:
|
||||
ovl_free_entry(oe);
|
||||
return err;
|
||||
}
|
||||
|
||||
int ovl_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
{
|
||||
struct ovl_fs *ofs = sb->s_fs_info;
|
||||
int err;
|
||||
|
||||
err = -EIO;
|
||||
if (WARN_ON(fc->user_ns != current_user_ns()))
|
||||
goto out_err;
|
||||
|
||||
ovl_set_d_op(sb);
|
||||
|
||||
if (!ofs->creator_cred) {
|
||||
err = -ENOMEM;
|
||||
ofs->creator_cred = prepare_creds();
|
||||
if (!ofs->creator_cred)
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
with_ovl_creds(sb)
|
||||
err = ovl_fill_super_creds(fc, sb);
|
||||
|
||||
out_err:
|
||||
/*
|
||||
* Revert creds before calling ovl_free_fs() which will call
|
||||
* put_cred() and put_cred() requires that the cred's that are
|
||||
* put are not the caller's creds, i.e., current->cred.
|
||||
*/
|
||||
if (old_cred)
|
||||
ovl_revert_creds(old_cred);
|
||||
ovl_free_fs(ofs);
|
||||
sb->s_fs_info = NULL;
|
||||
if (err) {
|
||||
ovl_free_fs(ofs);
|
||||
sb->s_fs_info = NULL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@@ -69,11 +69,6 @@ const struct cred *ovl_override_creds(struct super_block *sb)
|
||||
return override_creds(ofs->creator_cred);
|
||||
}
|
||||
|
||||
void ovl_revert_creds(const struct cred *old_cred)
|
||||
{
|
||||
revert_creds(old_cred);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if underlying fs supports file handles and try to determine encoding
|
||||
* type, in order to deduce maximum inode number used by fs.
|
||||
@@ -1147,7 +1142,6 @@ fail:
|
||||
int ovl_nlink_start(struct dentry *dentry)
|
||||
{
|
||||
struct inode *inode = d_inode(dentry);
|
||||
const struct cred *old_cred;
|
||||
int err;
|
||||
|
||||
if (WARN_ON(!inode))
|
||||
@@ -1184,15 +1178,14 @@ int ovl_nlink_start(struct dentry *dentry)
|
||||
if (d_is_dir(dentry) || !ovl_test_flag(OVL_INDEX, inode))
|
||||
return 0;
|
||||
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
/*
|
||||
* The overlay inode nlink should be incremented/decremented IFF the
|
||||
* upper operation succeeds, along with nlink change of upper inode.
|
||||
* Therefore, before link/unlink/rename, we store the union nlink
|
||||
* value relative to the upper inode nlink in an upper inode xattr.
|
||||
*/
|
||||
err = ovl_set_nlink_upper(dentry);
|
||||
ovl_revert_creds(old_cred);
|
||||
with_ovl_creds(dentry->d_sb)
|
||||
err = ovl_set_nlink_upper(dentry);
|
||||
if (err)
|
||||
goto out_drop_write;
|
||||
|
||||
@@ -1213,11 +1206,8 @@ void ovl_nlink_end(struct dentry *dentry)
|
||||
ovl_drop_write(dentry);
|
||||
|
||||
if (ovl_test_flag(OVL_INDEX, inode) && inode->i_nlink == 0) {
|
||||
const struct cred *old_cred;
|
||||
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
ovl_cleanup_index(dentry);
|
||||
ovl_revert_creds(old_cred);
|
||||
with_ovl_creds(dentry->d_sb)
|
||||
ovl_cleanup_index(dentry);
|
||||
}
|
||||
|
||||
ovl_inode_unlock(inode);
|
||||
|
||||
@@ -41,13 +41,11 @@ static int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char
|
||||
struct dentry *upperdentry = ovl_i_dentry_upper(inode);
|
||||
struct dentry *realdentry = upperdentry ?: ovl_dentry_lower(dentry);
|
||||
struct path realpath;
|
||||
const struct cred *old_cred;
|
||||
|
||||
if (!value && !upperdentry) {
|
||||
ovl_path_lower(dentry, &realpath);
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
err = vfs_getxattr(mnt_idmap(realpath.mnt), realdentry, name, NULL, 0);
|
||||
ovl_revert_creds(old_cred);
|
||||
with_ovl_creds(dentry->d_sb)
|
||||
err = vfs_getxattr(mnt_idmap(realpath.mnt), realdentry, name, NULL, 0);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
}
|
||||
@@ -64,15 +62,14 @@ static int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
if (value) {
|
||||
err = ovl_do_setxattr(ofs, realdentry, name, value, size,
|
||||
flags);
|
||||
} else {
|
||||
WARN_ON(flags != XATTR_REPLACE);
|
||||
err = ovl_do_removexattr(ofs, realdentry, name);
|
||||
with_ovl_creds(dentry->d_sb) {
|
||||
if (value) {
|
||||
err = ovl_do_setxattr(ofs, realdentry, name, value, size, flags);
|
||||
} else {
|
||||
WARN_ON(flags != XATTR_REPLACE);
|
||||
err = ovl_do_removexattr(ofs, realdentry, name);
|
||||
}
|
||||
}
|
||||
ovl_revert_creds(old_cred);
|
||||
ovl_drop_write(dentry);
|
||||
|
||||
/* copy c/mtime */
|
||||
@@ -84,15 +81,11 @@ out:
|
||||
static int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name,
|
||||
void *value, size_t size)
|
||||
{
|
||||
ssize_t res;
|
||||
const struct cred *old_cred;
|
||||
struct path realpath;
|
||||
|
||||
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);
|
||||
ovl_revert_creds(old_cred);
|
||||
return res;
|
||||
with_ovl_creds(dentry->d_sb)
|
||||
return vfs_getxattr(mnt_idmap(realpath.mnt), realpath.dentry, name, value, size);
|
||||
}
|
||||
|
||||
static bool ovl_can_list(struct super_block *sb, const char *s)
|
||||
@@ -116,12 +109,10 @@ ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
|
||||
ssize_t res;
|
||||
size_t len;
|
||||
char *s;
|
||||
const struct cred *old_cred;
|
||||
size_t prefix_len, name_len;
|
||||
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
res = vfs_listxattr(realdentry, list, size);
|
||||
ovl_revert_creds(old_cred);
|
||||
with_ovl_creds(dentry->d_sb)
|
||||
res = vfs_listxattr(realdentry, list, size);
|
||||
if (res <= 0 || size == 0)
|
||||
return res;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user