diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c
index f213297d187e8d602f890dde83202b8e9471fbaa..9ad48d9202a99a6c63ccb07b9b31476f5ee524af 100644
--- a/fs/overlayfs/namei.c
+++ b/fs/overlayfs/namei.c
@@ -10,6 +10,7 @@
 #include <linux/fs.h>
 #include <linux/namei.h>
 #include <linux/xattr.h>
+#include <linux/ratelimit.h>
 #include "overlayfs.h"
 #include "ovl_entry.h"
 
@@ -19,8 +20,66 @@ struct ovl_lookup_data {
 	bool opaque;
 	bool stop;
 	bool last;
+	char *redirect;
 };
 
+static int ovl_check_redirect(struct dentry *dentry, struct ovl_lookup_data *d,
+			      size_t prelen, const char *post)
+{
+	int res;
+	char *s, *next, *buf = NULL;
+
+	res = vfs_getxattr(dentry, OVL_XATTR_REDIRECT, NULL, 0);
+	if (res < 0) {
+		if (res == -ENODATA || res == -EOPNOTSUPP)
+			return 0;
+		goto fail;
+	}
+	buf = kzalloc(prelen + res + strlen(post) + 1, GFP_TEMPORARY);
+	if (!buf)
+		return -ENOMEM;
+
+	if (res == 0)
+		goto invalid;
+
+	res = vfs_getxattr(dentry, OVL_XATTR_REDIRECT, buf, res);
+	if (res < 0)
+		goto fail;
+	if (res == 0)
+		goto invalid;
+	if (buf[0] == '/') {
+		for (s = buf; *s++ == '/'; s = next) {
+			next = strchrnul(s, '/');
+			if (s == next)
+				goto invalid;
+		}
+	} else {
+		if (strchr(buf, '/') != NULL)
+			goto invalid;
+
+		memmove(buf + prelen, buf, res);
+		memcpy(buf, d->name.name, prelen);
+	}
+
+	strcat(buf, post);
+	kfree(d->redirect);
+	d->redirect = buf;
+	d->name.name = d->redirect;
+	d->name.len = strlen(d->redirect);
+
+	return 0;
+
+err_free:
+	kfree(buf);
+	return 0;
+fail:
+	pr_warn_ratelimited("overlayfs: failed to get redirect (%i)\n", res);
+	goto err_free;
+invalid:
+	pr_warn_ratelimited("overlayfs: invalid redirect (%s)\n", buf);
+	goto err_free;
+}
+
 static bool ovl_is_opaquedir(struct dentry *dentry)
 {
 	int res;
@@ -38,6 +97,7 @@ static bool ovl_is_opaquedir(struct dentry *dentry)
 
 static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
 			     const char *name, unsigned int namelen,
+			     size_t prelen, const char *post,
 			     struct dentry **ret)
 {
 	struct dentry *this;
@@ -74,6 +134,9 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
 		d->stop = d->opaque = true;
 		goto out;
 	}
+	err = ovl_check_redirect(this, d, prelen, post);
+	if (err)
+		goto out_err;
 out:
 	*ret = this;
 	return 0;
@@ -91,7 +154,32 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
 static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d,
 			    struct dentry **ret)
 {
-	return ovl_lookup_single(base, d, d->name.name, d->name.len, ret);
+	const char *s = d->name.name;
+	struct dentry *dentry = NULL;
+	int err;
+
+	if (*s != '/')
+		return ovl_lookup_single(base, d, d->name.name, d->name.len,
+					 0, "", ret);
+
+	while (*s++ == '/' && !IS_ERR_OR_NULL(base) && d_can_lookup(base)) {
+		const char *next = strchrnul(s, '/');
+		size_t slen = strlen(s);
+
+		if (WARN_ON(slen > d->name.len) ||
+		    WARN_ON(strcmp(d->name.name + d->name.len - slen, s)))
+			return -EIO;
+
+		err = ovl_lookup_single(base, d, s, next - s,
+					d->name.len - slen, next, &base);
+		dput(dentry);
+		if (err)
+			return err;
+		dentry = base;
+		s = next;
+	}
+	*ret = dentry;
+	return 0;
 }
 
 /*
@@ -127,6 +215,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
 	unsigned int ctr = 0;
 	struct inode *inode = NULL;
 	bool upperopaque = false;
+	char *upperredirect = NULL;
 	struct dentry *this;
 	unsigned int i;
 	int err;
@@ -136,6 +225,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
 		.opaque = false,
 		.stop = false,
 		.last = !poe->numlower,
+		.redirect = NULL,
 	};
 
 	if (dentry->d_name.len > ofs->namelen)
@@ -153,12 +243,20 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
 			err = -EREMOTE;
 			goto out;
 		}
+
+		if (d.redirect) {
+			upperredirect = kstrdup(d.redirect, GFP_KERNEL);
+			if (!upperredirect)
+				goto out_put_upper;
+			if (d.redirect[0] == '/')
+				poe = dentry->d_sb->s_root->d_fsdata;
+		}
 		upperopaque = d.opaque;
 	}
 
 	if (!d.stop && poe->numlower) {
 		err = -ENOMEM;
-		stack = kcalloc(poe->numlower, sizeof(struct path),
+		stack = kcalloc(ofs->numlower, sizeof(struct path),
 				GFP_TEMPORARY);
 		if (!stack)
 			goto out_put_upper;
@@ -178,6 +276,22 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
 		stack[ctr].dentry = this;
 		stack[ctr].mnt = lowerpath.mnt;
 		ctr++;
+
+		if (d.stop)
+			break;
+
+		if (d.redirect &&
+		    d.redirect[0] == '/' &&
+		    poe != dentry->d_sb->s_root->d_fsdata) {
+			poe = dentry->d_sb->s_root->d_fsdata;
+
+			/* Find the current layer on the root dentry */
+			for (i = 0; i < poe->numlower; i++)
+				if (poe->lowerstack[i].mnt == lowerpath.mnt)
+					break;
+			if (WARN_ON(i == poe->numlower))
+				break;
+		}
 	}
 
 	oe = ovl_alloc_entry(ctr);
@@ -208,9 +322,11 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
 
 	revert_creds(old_cred);
 	oe->opaque = upperopaque;
+	oe->redirect = upperredirect;
 	oe->__upperdentry = upperdentry;
 	memcpy(oe->lowerstack, stack, sizeof(struct path) * ctr);
 	kfree(stack);
+	kfree(d.redirect);
 	dentry->d_fsdata = oe;
 	d_add(dentry, inode);
 
@@ -224,7 +340,9 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
 	kfree(stack);
 out_put_upper:
 	dput(upperdentry);
+	kfree(upperredirect);
 out:
+	kfree(d.redirect);
 	revert_creds(old_cred);
 	return ERR_PTR(err);
 }
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index f6e4d3539a251661c072cf994cb0934732cfeffe..e76d9d529e64cbcdc8c0f0e0560e26a96e9baad4 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -19,6 +19,7 @@ enum ovl_path_type {
 
 #define OVL_XATTR_PREFIX XATTR_TRUSTED_PREFIX "overlay."
 #define OVL_XATTR_OPAQUE OVL_XATTR_PREFIX "opaque"
+#define OVL_XATTR_REDIRECT OVL_XATTR_PREFIX "redirect"
 
 #define OVL_ISUPPER_MASK 1UL
 
diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h
index b10745edfc9361256bc988baf26d591ae9f8495c..eb29882b6a54dda74287d618197039a46277ba06 100644
--- a/fs/overlayfs/ovl_entry.h
+++ b/fs/overlayfs/ovl_entry.h
@@ -35,6 +35,7 @@ struct ovl_entry {
 	union {
 		struct {
 			u64 version;
+			const char *redirect;
 			bool opaque;
 		};
 		struct rcu_head rcu;
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index aadb25413e6e6a4e9dc20e46831be4a30b9f0ba1..4e44e865b716cb17cd9bcfb7470961d2926b95f5 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -37,6 +37,7 @@ static void ovl_dentry_release(struct dentry *dentry)
 		unsigned int i;
 
 		dput(oe->__upperdentry);
+		kfree(oe->redirect);
 		for (i = 0; i < oe->numlower; i++)
 			dput(oe->lowerstack[i].dentry);
 		kfree_rcu(oe, rcu);