Commit d0deeb80 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
  ...
parents a8058f84 2579e21b
Loading
Loading
Loading
Loading
+33 −37
Original line number Diff line number Diff line
@@ -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, &copy_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);
	}

	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_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;
		}

		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;
}
+191 −173
Original line number Diff line number Diff line
@@ -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,48 +581,59 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
	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);

	return override_creds(no_free_ptr(override_cred));
}

	/*
	 * 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));
static void ovl_revert_creator_creds(const struct cred *old_cred)
{
	const struct cred *override_cred;

	return 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".
@@ -630,10 +641,9 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
		if (origin) {
			err = ovl_set_impure(parent, ovl_dentry_upper(parent));
			if (err)
			goto out_revert_creds;
				return err;
		}

	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 (!ovl_dentry_is_whiteout(dentry))
		err = ovl_create_upper(dentry, inode, attr);
	else
		err = ovl_create_over_whiteout(dentry, inode, attr);
		if (attr->hardlink)
			return ovl_create_handle_whiteouts(dentry, inode, attr);

out_revert_creds:
	ovl_revert_creds(old_cred);
	put_cred(new_cred);
		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);
		}
	}
	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);
	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);
	ovl_revert_creds(old_cred);
	}
	if (!err) {
		if (is_dir)
			clear_nlink(dentry->d_inode);
@@ -1109,101 +1105,107 @@ 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);

	if (!list_empty(&list)) {
		opaquedir = ovl_clear_empty(new, &list);
		err = PTR_ERR(opaquedir);
		if (IS_ERR(opaquedir)) {
			opaquedir = NULL;
			goto out_revert_creds;
		}
	return 0;
}

	old_upperdir = ovl_dentry_upper(old->d_parent);
	new_upperdir = ovl_dentry_upper(new->d_parent);
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;
	}

	if (!samedir) {
		/*
@@ -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,13 +1364,10 @@ 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,
@@ -1355,13 +1375,12 @@ static int ovl_create_tmpfile(struct file *file, struct dentry *dentry,
			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;
				return err;

			of = ovl_file_alloc(realfile);
			if (!of) {
				fput(realfile);
		err = -ENOMEM;
		goto out_revert_creds;
				return -ENOMEM;
			}

			/* ovl_instantiate() consumes the newdentry reference on success */
@@ -1373,9 +1392,8 @@ static int ovl_create_tmpfile(struct file *file, struct dentry *dentry,
				dput(newdentry);
				ovl_file_free(of);
			}
out_revert_creds:
	ovl_revert_creds(old_cred);
	put_cred(new_cred);
		}
	}
	return err;
}

+40 −57
Original line number Diff line number Diff line
@@ -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,7 +38,7 @@ 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);
	with_ovl_creds(inode->i_sb) {
		real_idmap = mnt_idmap(realpath->mnt);
		err = inode_permission(real_idmap, realinode, MAY_OPEN | acc_mode);
		if (err) {
@@ -51,7 +50,7 @@ static struct file *ovl_open_realfile(const struct file *file,
			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);
	with_ovl_creds(inode->i_sb)
		ret = vfs_llseek(realfile, offset, whence);
	ovl_revert_creds(old_cred);

	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);
	with_ovl_creds(inode->i_sb)
		ret = vfs_fallocate(realfile, mode, offset, len);
	ovl_revert_creds(old_cred);

	/* Update size */
	ovl_file_modified(file);
@@ -517,18 +508,13 @@ 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 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,7 +550,7 @@ 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);
	with_ovl_creds(file_inode(file_out)->i_sb) {
		switch (op) {
		case OVL_COPY:
			ret = vfs_copy_file_range(realfile_in, pos_in,
@@ -583,7 +568,7 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in,
							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);
		with_ovl_creds(file_inode(file)->i_sb)
			err = realfile->f_op->flush(realfile, id);
		ovl_revert_creds(old_cred);
	}

	return err;
+49 −69

File changed.

Preview size limit exceeded, changes collapsed.

+199 −203

File changed.

Preview size limit exceeded, changes collapsed.

Loading