Skip to content

Commit 02b69b2

Browse files
author
Miklos Szeredi
committed
ovl: lookup redirects
If a directory has the "trusted.overlay.redirect" xattr, it means that the value of the xattr should be used to find the underlying directory on the next lower layer. The redirect may be relative or absolute. Absolute redirects begin with a slash. A relative redirect means: instead of the current dentry's name use the value of the redirect to find the directory in the next lower layer. Relative redirects must not contain a slash. An absolute redirect means: look up the directory relative to the root of the overlay using the value of the redirect in the next lower layer. Redirects work on lower layers as well. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
1 parent e28edc4 commit 02b69b2

File tree

4 files changed

+123
-2
lines changed

4 files changed

+123
-2
lines changed

fs/overlayfs/namei.c

Lines changed: 120 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <linux/fs.h>
1111
#include <linux/namei.h>
1212
#include <linux/xattr.h>
13+
#include <linux/ratelimit.h>
1314
#include "overlayfs.h"
1415
#include "ovl_entry.h"
1516

@@ -19,8 +20,66 @@ struct ovl_lookup_data {
1920
bool opaque;
2021
bool stop;
2122
bool last;
23+
char *redirect;
2224
};
2325

26+
static int ovl_check_redirect(struct dentry *dentry, struct ovl_lookup_data *d,
27+
size_t prelen, const char *post)
28+
{
29+
int res;
30+
char *s, *next, *buf = NULL;
31+
32+
res = vfs_getxattr(dentry, OVL_XATTR_REDIRECT, NULL, 0);
33+
if (res < 0) {
34+
if (res == -ENODATA || res == -EOPNOTSUPP)
35+
return 0;
36+
goto fail;
37+
}
38+
buf = kzalloc(prelen + res + strlen(post) + 1, GFP_TEMPORARY);
39+
if (!buf)
40+
return -ENOMEM;
41+
42+
if (res == 0)
43+
goto invalid;
44+
45+
res = vfs_getxattr(dentry, OVL_XATTR_REDIRECT, buf, res);
46+
if (res < 0)
47+
goto fail;
48+
if (res == 0)
49+
goto invalid;
50+
if (buf[0] == '/') {
51+
for (s = buf; *s++ == '/'; s = next) {
52+
next = strchrnul(s, '/');
53+
if (s == next)
54+
goto invalid;
55+
}
56+
} else {
57+
if (strchr(buf, '/') != NULL)
58+
goto invalid;
59+
60+
memmove(buf + prelen, buf, res);
61+
memcpy(buf, d->name.name, prelen);
62+
}
63+
64+
strcat(buf, post);
65+
kfree(d->redirect);
66+
d->redirect = buf;
67+
d->name.name = d->redirect;
68+
d->name.len = strlen(d->redirect);
69+
70+
return 0;
71+
72+
err_free:
73+
kfree(buf);
74+
return 0;
75+
fail:
76+
pr_warn_ratelimited("overlayfs: failed to get redirect (%i)\n", res);
77+
goto err_free;
78+
invalid:
79+
pr_warn_ratelimited("overlayfs: invalid redirect (%s)\n", buf);
80+
goto err_free;
81+
}
82+
2483
static bool ovl_is_opaquedir(struct dentry *dentry)
2584
{
2685
int res;
@@ -38,6 +97,7 @@ static bool ovl_is_opaquedir(struct dentry *dentry)
3897

3998
static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
4099
const char *name, unsigned int namelen,
100+
size_t prelen, const char *post,
41101
struct dentry **ret)
42102
{
43103
struct dentry *this;
@@ -74,6 +134,9 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
74134
d->stop = d->opaque = true;
75135
goto out;
76136
}
137+
err = ovl_check_redirect(this, d, prelen, post);
138+
if (err)
139+
goto out_err;
77140
out:
78141
*ret = this;
79142
return 0;
@@ -91,7 +154,32 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
91154
static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d,
92155
struct dentry **ret)
93156
{
94-
return ovl_lookup_single(base, d, d->name.name, d->name.len, ret);
157+
const char *s = d->name.name;
158+
struct dentry *dentry = NULL;
159+
int err;
160+
161+
if (*s != '/')
162+
return ovl_lookup_single(base, d, d->name.name, d->name.len,
163+
0, "", ret);
164+
165+
while (*s++ == '/' && !IS_ERR_OR_NULL(base) && d_can_lookup(base)) {
166+
const char *next = strchrnul(s, '/');
167+
size_t slen = strlen(s);
168+
169+
if (WARN_ON(slen > d->name.len) ||
170+
WARN_ON(strcmp(d->name.name + d->name.len - slen, s)))
171+
return -EIO;
172+
173+
err = ovl_lookup_single(base, d, s, next - s,
174+
d->name.len - slen, next, &base);
175+
dput(dentry);
176+
if (err)
177+
return err;
178+
dentry = base;
179+
s = next;
180+
}
181+
*ret = dentry;
182+
return 0;
95183
}
96184

97185
/*
@@ -127,6 +215,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
127215
unsigned int ctr = 0;
128216
struct inode *inode = NULL;
129217
bool upperopaque = false;
218+
char *upperredirect = NULL;
130219
struct dentry *this;
131220
unsigned int i;
132221
int err;
@@ -136,6 +225,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
136225
.opaque = false,
137226
.stop = false,
138227
.last = !poe->numlower,
228+
.redirect = NULL,
139229
};
140230

141231
if (dentry->d_name.len > ofs->namelen)
@@ -153,12 +243,20 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
153243
err = -EREMOTE;
154244
goto out;
155245
}
246+
247+
if (d.redirect) {
248+
upperredirect = kstrdup(d.redirect, GFP_KERNEL);
249+
if (!upperredirect)
250+
goto out_put_upper;
251+
if (d.redirect[0] == '/')
252+
poe = dentry->d_sb->s_root->d_fsdata;
253+
}
156254
upperopaque = d.opaque;
157255
}
158256

159257
if (!d.stop && poe->numlower) {
160258
err = -ENOMEM;
161-
stack = kcalloc(poe->numlower, sizeof(struct path),
259+
stack = kcalloc(ofs->numlower, sizeof(struct path),
162260
GFP_TEMPORARY);
163261
if (!stack)
164262
goto out_put_upper;
@@ -178,6 +276,22 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
178276
stack[ctr].dentry = this;
179277
stack[ctr].mnt = lowerpath.mnt;
180278
ctr++;
279+
280+
if (d.stop)
281+
break;
282+
283+
if (d.redirect &&
284+
d.redirect[0] == '/' &&
285+
poe != dentry->d_sb->s_root->d_fsdata) {
286+
poe = dentry->d_sb->s_root->d_fsdata;
287+
288+
/* Find the current layer on the root dentry */
289+
for (i = 0; i < poe->numlower; i++)
290+
if (poe->lowerstack[i].mnt == lowerpath.mnt)
291+
break;
292+
if (WARN_ON(i == poe->numlower))
293+
break;
294+
}
181295
}
182296

183297
oe = ovl_alloc_entry(ctr);
@@ -208,9 +322,11 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
208322

209323
revert_creds(old_cred);
210324
oe->opaque = upperopaque;
325+
oe->redirect = upperredirect;
211326
oe->__upperdentry = upperdentry;
212327
memcpy(oe->lowerstack, stack, sizeof(struct path) * ctr);
213328
kfree(stack);
329+
kfree(d.redirect);
214330
dentry->d_fsdata = oe;
215331
d_add(dentry, inode);
216332

@@ -224,7 +340,9 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
224340
kfree(stack);
225341
out_put_upper:
226342
dput(upperdentry);
343+
kfree(upperredirect);
227344
out:
345+
kfree(d.redirect);
228346
revert_creds(old_cred);
229347
return ERR_PTR(err);
230348
}

fs/overlayfs/overlayfs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ enum ovl_path_type {
1919

2020
#define OVL_XATTR_PREFIX XATTR_TRUSTED_PREFIX "overlay."
2121
#define OVL_XATTR_OPAQUE OVL_XATTR_PREFIX "opaque"
22+
#define OVL_XATTR_REDIRECT OVL_XATTR_PREFIX "redirect"
2223

2324
#define OVL_ISUPPER_MASK 1UL
2425

fs/overlayfs/ovl_entry.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ struct ovl_entry {
3535
union {
3636
struct {
3737
u64 version;
38+
const char *redirect;
3839
bool opaque;
3940
};
4041
struct rcu_head rcu;

fs/overlayfs/super.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ static void ovl_dentry_release(struct dentry *dentry)
3737
unsigned int i;
3838

3939
dput(oe->__upperdentry);
40+
kfree(oe->redirect);
4041
for (i = 0; i < oe->numlower; i++)
4142
dput(oe->lowerstack[i].dentry);
4243
kfree_rcu(oe, rcu);

0 commit comments

Comments
 (0)