Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c9f27f9f authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull overlayfs fixes from Miklos Szeredi:
 "Fix several issues, most of them introduced in the last release"

* 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs:
  ovl: do not cleanup unsupported index entries
  ovl: handle ENOENT on index lookup
  ovl: fix EIO from lookup of non-indexed upper
  ovl: Return -ENOMEM if an allocation fails ovl_lookup()
  ovl: add NULL check in ovl_alloc_inode
parents a9af9835 fa0096e3
Loading
Loading
Loading
Loading
+16 −4
Original line number Diff line number Diff line
@@ -598,18 +598,30 @@ static bool ovl_verify_inode(struct inode *inode, struct dentry *lowerdentry,
	return true;
}

struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry)
struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry,
			    struct dentry *index)
{
	struct dentry *lowerdentry = ovl_dentry_lower(dentry);
	struct inode *realinode = upperdentry ? d_inode(upperdentry) : NULL;
	struct inode *inode;
	/* Already indexed or could be indexed on copy up? */
	bool indexed = (index || (ovl_indexdir(dentry->d_sb) && !upperdentry));

	if (WARN_ON(upperdentry && indexed && !lowerdentry))
		return ERR_PTR(-EIO);

	if (!realinode)
		realinode = d_inode(lowerdentry);

	if (!S_ISDIR(realinode->i_mode) &&
	    (upperdentry || (lowerdentry && ovl_indexdir(dentry->d_sb)))) {
		struct inode *key = d_inode(lowerdentry ?: upperdentry);
	/*
	 * Copy up origin (lower) may exist for non-indexed upper, but we must
	 * not use lower as hash key in that case.
	 * Hash inodes that are or could be indexed by origin inode and
	 * non-indexed upper inodes that could be hard linked by upper inode.
	 */
	if (!S_ISDIR(realinode->i_mode) && (upperdentry || indexed)) {
		struct inode *key = d_inode(indexed ? lowerdentry :
						      upperdentry);
		unsigned int nlink;

		inode = iget5_locked(dentry->d_sb, (unsigned long) key,
+16 −16
Original line number Diff line number Diff line
@@ -405,14 +405,13 @@ int ovl_verify_index(struct dentry *index, struct path *lowerstack,
	 * be treated as stale (i.e. after unlink of the overlay inode).
	 * We don't know the verification rules for directory and whiteout
	 * index entries, because they have not been implemented yet, so return
	 * EROFS if those entries are found to avoid corrupting an index that
	 * was created by a newer kernel.
	 * EINVAL if those entries are found to abort the mount to avoid
	 * corrupting an index that was created by a newer kernel.
	 */
	err = -EROFS;
	err = -EINVAL;
	if (d_is_dir(index) || ovl_is_whiteout(index))
		goto fail;

	err = -EINVAL;
	if (index->d_name.len < sizeof(struct ovl_fh)*2)
		goto fail;

@@ -507,6 +506,10 @@ static struct dentry *ovl_lookup_index(struct dentry *dentry,
	index = lookup_one_len_unlocked(name.name, ofs->indexdir, name.len);
	if (IS_ERR(index)) {
		err = PTR_ERR(index);
		if (err == -ENOENT) {
			index = NULL;
			goto out;
		}
		pr_warn_ratelimited("overlayfs: failed inode index lookup (ino=%lu, key=%*s, err=%i);\n"
				    "overlayfs: mount with '-o index=off' to disable inodes index.\n",
				    d_inode(origin)->i_ino, name.len, name.name,
@@ -516,18 +519,9 @@ static struct dentry *ovl_lookup_index(struct dentry *dentry,

	inode = d_inode(index);
	if (d_is_negative(index)) {
		if (upper && d_inode(origin)->i_nlink > 1) {
			pr_warn_ratelimited("overlayfs: hard link with origin but no index (ino=%lu).\n",
					    d_inode(origin)->i_ino);
			goto fail;
		}

		dput(index);
		index = NULL;
		goto out_dput;
	} else if (upper && d_inode(upper) != inode) {
		pr_warn_ratelimited("overlayfs: wrong index found (index=%pd2, ino=%lu, upper ino=%lu).\n",
				    index, inode->i_ino, d_inode(upper)->i_ino);
		goto fail;
		goto out_dput;
	} else if (ovl_dentry_weird(index) || ovl_is_whiteout(index) ||
		   ((inode->i_mode ^ d_inode(origin)->i_mode) & S_IFMT)) {
		/*
@@ -547,6 +541,11 @@ static struct dentry *ovl_lookup_index(struct dentry *dentry,
	kfree(name.name);
	return index;

out_dput:
	dput(index);
	index = NULL;
	goto out;

fail:
	dput(index);
	index = ERR_PTR(-EIO);
@@ -635,6 +634,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
		}

		if (d.redirect) {
			err = -ENOMEM;
			upperredirect = kstrdup(d.redirect, GFP_KERNEL);
			if (!upperredirect)
				goto out_put_upper;
@@ -709,7 +709,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
		upperdentry = dget(index);

	if (upperdentry || ctr) {
		inode = ovl_get_inode(dentry, upperdentry);
		inode = ovl_get_inode(dentry, upperdentry, index);
		err = PTR_ERR(inode);
		if (IS_ERR(inode))
			goto out_free_oe;
+2 −1
Original line number Diff line number Diff line
@@ -286,7 +286,8 @@ int ovl_update_time(struct inode *inode, struct timespec *ts, int flags);
bool ovl_is_private_xattr(const char *name);

struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev);
struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry);
struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry,
			    struct dentry *index);
static inline void ovl_copyattr(struct inode *from, struct inode *to)
{
	to->i_uid = from->i_uid;
+5 −6
Original line number Diff line number Diff line
@@ -1021,13 +1021,12 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt,
			break;
		}
		err = ovl_verify_index(index, lowerstack, numlower);
		if (err) {
			if (err == -EROFS)
				break;
		/* Cleanup stale and orphan index entries */
		if (err && (err == -ESTALE || err == -ENOENT))
			err = ovl_cleanup(dir, index);
		if (err)
			break;
		}

		dput(index);
		index = NULL;
	}
+3 −0
Original line number Diff line number Diff line
@@ -174,6 +174,9 @@ static struct inode *ovl_alloc_inode(struct super_block *sb)
{
	struct ovl_inode *oi = kmem_cache_alloc(ovl_inode_cachep, GFP_KERNEL);

	if (!oi)
		return NULL;

	oi->cache = NULL;
	oi->redirect = NULL;
	oi->version = 0;