Skip to content

Commit dad02fa

Browse files
alexlarssonamir73il
authored andcommitted
ovl: Support escaped overlay.* xattrs
There are cases where you want to use an overlayfs mount as a lowerdir for another overlayfs mount. For example, if the system rootfs is on overlayfs due to composefs, or to make it volatile (via tmps), then you cannot currently store a lowerdir on the rootfs. This means you can't e.g. store on the rootfs a prepared container image for use using overlayfs. To work around this, we introduce an escapment mechanism for overlayfs xattrs. Whenever the lower/upper dir has a xattr named "overlay.overlay.XYZ", we list it as "overlay.XYZ" in listxattrs, and when the user calls getxattr or setxattr on "overlay.XYZ", we apply to "overlay.overlay.XYZ" in the backing directories. This allows storing any kind of overlay xattrs in a overlayfs mount that can be used as a lowerdir in another mount. It is possible to stack this mechanism multiple times, such that "overlay.overlay.overlay.XYZ" will survive two levels of overlay mounts, however this is not all that useful in practice because of stack depth limitations of overlayfs mounts. Note: These escaped xattrs are copied to upper during copy-up. Signed-off-by: Alexander Larsson <[email protected]> Reviewed-by: Amir Goldstein <[email protected]> Signed-off-by: Amir Goldstein <[email protected]>
1 parent d431e65 commit dad02fa

File tree

2 files changed

+85
-3
lines changed

2 files changed

+85
-3
lines changed

fs/overlayfs/overlayfs.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ enum ovl_path_type {
3232
#define OVL_XATTR_USER_PREFIX XATTR_USER_PREFIX OVL_XATTR_NAMESPACE
3333
#define OVL_XATTR_USER_PREFIX_LEN (sizeof(OVL_XATTR_USER_PREFIX) - 1)
3434

35+
#define OVL_XATTR_ESCAPE_PREFIX OVL_XATTR_NAMESPACE
36+
#define OVL_XATTR_ESCAPE_PREFIX_LEN (sizeof(OVL_XATTR_ESCAPE_PREFIX) - 1)
37+
#define OVL_XATTR_ESCAPE_TRUSTED_PREFIX OVL_XATTR_TRUSTED_PREFIX OVL_XATTR_ESCAPE_PREFIX
38+
#define OVL_XATTR_ESCAPE_TRUSTED_PREFIX_LEN (sizeof(OVL_XATTR_ESCAPE_TRUSTED_PREFIX) - 1)
39+
#define OVL_XATTR_ESCAPE_USER_PREFIX OVL_XATTR_USER_PREFIX OVL_XATTR_ESCAPE_PREFIX
40+
#define OVL_XATTR_ESCAPE_USER_PREFIX_LEN (sizeof(OVL_XATTR_ESCAPE_USER_PREFIX) - 1)
41+
3542
enum ovl_xattr {
3643
OVL_XATTR_OPAQUE,
3744
OVL_XATTR_REDIRECT,

fs/overlayfs/xattrs.c

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,19 @@
44
#include <linux/xattr.h>
55
#include "overlayfs.h"
66

7-
bool ovl_is_private_xattr(struct super_block *sb, const char *name)
7+
static bool ovl_is_escaped_xattr(struct super_block *sb, const char *name)
8+
{
9+
struct ovl_fs *ofs = sb->s_fs_info;
10+
11+
if (ofs->config.userxattr)
12+
return strncmp(name, OVL_XATTR_ESCAPE_USER_PREFIX,
13+
OVL_XATTR_ESCAPE_USER_PREFIX_LEN) == 0;
14+
else
15+
return strncmp(name, OVL_XATTR_ESCAPE_TRUSTED_PREFIX,
16+
OVL_XATTR_ESCAPE_TRUSTED_PREFIX_LEN - 1) == 0;
17+
}
18+
19+
static bool ovl_is_own_xattr(struct super_block *sb, const char *name)
820
{
921
struct ovl_fs *ofs = OVL_FS(sb);
1022

@@ -16,6 +28,11 @@ bool ovl_is_private_xattr(struct super_block *sb, const char *name)
1628
OVL_XATTR_TRUSTED_PREFIX_LEN) == 0;
1729
}
1830

31+
bool ovl_is_private_xattr(struct super_block *sb, const char *name)
32+
{
33+
return ovl_is_own_xattr(sb, name) && !ovl_is_escaped_xattr(sb, name);
34+
}
35+
1936
static int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
2037
const void *value, size_t size, int flags)
2138
{
@@ -95,17 +112,22 @@ static bool ovl_can_list(struct super_block *sb, const char *s)
95112
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
96113
{
97114
struct dentry *realdentry = ovl_dentry_real(dentry);
115+
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
98116
ssize_t res;
99117
size_t len;
100118
char *s;
101119
const struct cred *old_cred;
120+
size_t prefix_len, name_len;
102121

103122
old_cred = ovl_override_creds(dentry->d_sb);
104123
res = vfs_listxattr(realdentry, list, size);
105124
revert_creds(old_cred);
106125
if (res <= 0 || size == 0)
107126
return res;
108127

128+
prefix_len = ofs->config.userxattr ?
129+
OVL_XATTR_USER_PREFIX_LEN : OVL_XATTR_TRUSTED_PREFIX_LEN;
130+
109131
/* filter out private xattrs */
110132
for (s = list, len = res; len;) {
111133
size_t slen = strnlen(s, len) + 1;
@@ -118,6 +140,12 @@ ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
118140
if (!ovl_can_list(dentry->d_sb, s)) {
119141
res -= slen;
120142
memmove(s, s + slen, len);
143+
} else if (ovl_is_escaped_xattr(dentry->d_sb, s)) {
144+
res -= OVL_XATTR_ESCAPE_PREFIX_LEN;
145+
name_len = slen - prefix_len - OVL_XATTR_ESCAPE_PREFIX_LEN;
146+
s += prefix_len;
147+
memmove(s, s + OVL_XATTR_ESCAPE_PREFIX_LEN, name_len + len);
148+
s += name_len;
121149
} else {
122150
s += slen;
123151
}
@@ -126,11 +154,47 @@ ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
126154
return res;
127155
}
128156

157+
static char *ovl_xattr_escape_name(const char *prefix, const char *name)
158+
{
159+
size_t prefix_len = strlen(prefix);
160+
size_t name_len = strlen(name);
161+
size_t escaped_len;
162+
char *escaped, *s;
163+
164+
escaped_len = prefix_len + OVL_XATTR_ESCAPE_PREFIX_LEN + name_len;
165+
if (escaped_len > XATTR_NAME_MAX)
166+
return ERR_PTR(-EOPNOTSUPP);
167+
168+
escaped = kmalloc(escaped_len + 1, GFP_KERNEL);
169+
if (escaped == NULL)
170+
return ERR_PTR(-ENOMEM);
171+
172+
s = escaped;
173+
memcpy(s, prefix, prefix_len);
174+
s += prefix_len;
175+
memcpy(s, OVL_XATTR_ESCAPE_PREFIX, OVL_XATTR_ESCAPE_PREFIX_LEN);
176+
s += OVL_XATTR_ESCAPE_PREFIX_LEN;
177+
memcpy(s, name, name_len + 1);
178+
179+
return escaped;
180+
}
181+
129182
static int ovl_own_xattr_get(const struct xattr_handler *handler,
130183
struct dentry *dentry, struct inode *inode,
131184
const char *name, void *buffer, size_t size)
132185
{
133-
return -EOPNOTSUPP;
186+
char *escaped;
187+
int r;
188+
189+
escaped = ovl_xattr_escape_name(handler->prefix, name);
190+
if (IS_ERR(escaped))
191+
return PTR_ERR(escaped);
192+
193+
r = ovl_xattr_get(dentry, inode, escaped, buffer, size);
194+
195+
kfree(escaped);
196+
197+
return r;
134198
}
135199

136200
static int ovl_own_xattr_set(const struct xattr_handler *handler,
@@ -139,7 +203,18 @@ static int ovl_own_xattr_set(const struct xattr_handler *handler,
139203
const char *name, const void *value,
140204
size_t size, int flags)
141205
{
142-
return -EOPNOTSUPP;
206+
char *escaped;
207+
int r;
208+
209+
escaped = ovl_xattr_escape_name(handler->prefix, name);
210+
if (IS_ERR(escaped))
211+
return PTR_ERR(escaped);
212+
213+
r = ovl_xattr_set(dentry, inode, escaped, value, size, flags);
214+
215+
kfree(escaped);
216+
217+
return r;
143218
}
144219

145220
static int ovl_other_xattr_get(const struct xattr_handler *handler,

0 commit comments

Comments
 (0)