Commit | Line | Data |
---|---|---|
420a62dd AG |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | ||
3 | #include <linux/fs.h> | |
4 | #include <linux/xattr.h> | |
5 | #include "overlayfs.h" | |
6 | ||
dad02fad AL |
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) | |
420a62dd AG |
20 | { |
21 | struct ovl_fs *ofs = OVL_FS(sb); | |
22 | ||
23 | if (ofs->config.userxattr) | |
24 | return strncmp(name, OVL_XATTR_USER_PREFIX, | |
d431e652 | 25 | OVL_XATTR_USER_PREFIX_LEN) == 0; |
420a62dd AG |
26 | else |
27 | return strncmp(name, OVL_XATTR_TRUSTED_PREFIX, | |
d431e652 | 28 | OVL_XATTR_TRUSTED_PREFIX_LEN) == 0; |
420a62dd AG |
29 | } |
30 | ||
dad02fad AL |
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 | ||
420a62dd AG |
36 | static int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name, |
37 | const void *value, size_t size, int flags) | |
38 | { | |
39 | int err; | |
40 | struct ovl_fs *ofs = OVL_FS(dentry->d_sb); | |
41 | struct dentry *upperdentry = ovl_i_dentry_upper(inode); | |
42 | struct dentry *realdentry = upperdentry ?: ovl_dentry_lower(dentry); | |
43 | struct path realpath; | |
44 | const struct cred *old_cred; | |
45 | ||
46 | if (!value && !upperdentry) { | |
47 | ovl_path_lower(dentry, &realpath); | |
48 | old_cred = ovl_override_creds(dentry->d_sb); | |
49 | err = vfs_getxattr(mnt_idmap(realpath.mnt), realdentry, name, NULL, 0); | |
50 | revert_creds(old_cred); | |
51 | if (err < 0) | |
52 | goto out; | |
53 | } | |
54 | ||
55 | if (!upperdentry) { | |
56 | err = ovl_copy_up(dentry); | |
57 | if (err) | |
58 | goto out; | |
59 | ||
60 | realdentry = ovl_dentry_upper(dentry); | |
61 | } | |
62 | ||
63 | err = ovl_want_write(dentry); | |
64 | if (err) | |
65 | goto out; | |
66 | ||
67 | old_cred = ovl_override_creds(dentry->d_sb); | |
68 | if (value) { | |
69 | err = ovl_do_setxattr(ofs, realdentry, name, value, size, | |
70 | flags); | |
71 | } else { | |
72 | WARN_ON(flags != XATTR_REPLACE); | |
73 | err = ovl_do_removexattr(ofs, realdentry, name); | |
74 | } | |
75 | revert_creds(old_cred); | |
76 | ovl_drop_write(dentry); | |
77 | ||
78 | /* copy c/mtime */ | |
79 | ovl_copyattr(inode); | |
80 | out: | |
81 | return err; | |
82 | } | |
83 | ||
84 | static int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name, | |
85 | void *value, size_t size) | |
86 | { | |
87 | ssize_t res; | |
88 | const struct cred *old_cred; | |
89 | struct path realpath; | |
90 | ||
91 | ovl_i_path_real(inode, &realpath); | |
92 | old_cred = ovl_override_creds(dentry->d_sb); | |
93 | res = vfs_getxattr(mnt_idmap(realpath.mnt), realpath.dentry, name, value, size); | |
94 | revert_creds(old_cred); | |
95 | return res; | |
96 | } | |
97 | ||
98 | static bool ovl_can_list(struct super_block *sb, const char *s) | |
99 | { | |
100 | /* Never list private (.overlay) */ | |
101 | if (ovl_is_private_xattr(sb, s)) | |
102 | return false; | |
103 | ||
104 | /* List all non-trusted xattrs */ | |
105 | if (strncmp(s, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) != 0) | |
106 | return true; | |
107 | ||
108 | /* list other trusted for superuser only */ | |
109 | return ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN); | |
110 | } | |
111 | ||
112 | ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size) | |
113 | { | |
114 | struct dentry *realdentry = ovl_dentry_real(dentry); | |
dad02fad | 115 | struct ovl_fs *ofs = OVL_FS(dentry->d_sb); |
420a62dd AG |
116 | ssize_t res; |
117 | size_t len; | |
118 | char *s; | |
119 | const struct cred *old_cred; | |
dad02fad | 120 | size_t prefix_len, name_len; |
420a62dd AG |
121 | |
122 | old_cred = ovl_override_creds(dentry->d_sb); | |
123 | res = vfs_listxattr(realdentry, list, size); | |
124 | revert_creds(old_cred); | |
125 | if (res <= 0 || size == 0) | |
126 | return res; | |
127 | ||
dad02fad AL |
128 | prefix_len = ofs->config.userxattr ? |
129 | OVL_XATTR_USER_PREFIX_LEN : OVL_XATTR_TRUSTED_PREFIX_LEN; | |
130 | ||
420a62dd AG |
131 | /* filter out private xattrs */ |
132 | for (s = list, len = res; len;) { | |
133 | size_t slen = strnlen(s, len) + 1; | |
134 | ||
135 | /* underlying fs providing us with an broken xattr list? */ | |
136 | if (WARN_ON(slen > len)) | |
137 | return -EIO; | |
138 | ||
139 | len -= slen; | |
140 | if (!ovl_can_list(dentry->d_sb, s)) { | |
141 | res -= slen; | |
142 | memmove(s, s + slen, len); | |
dad02fad AL |
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; | |
420a62dd AG |
149 | } else { |
150 | s += slen; | |
151 | } | |
152 | } | |
153 | ||
154 | return res; | |
155 | } | |
156 | ||
dad02fad AL |
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 | ||
420a62dd AG |
182 | static int ovl_own_xattr_get(const struct xattr_handler *handler, |
183 | struct dentry *dentry, struct inode *inode, | |
184 | const char *name, void *buffer, size_t size) | |
185 | { | |
dad02fad AL |
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; | |
420a62dd AG |
198 | } |
199 | ||
200 | static int ovl_own_xattr_set(const struct xattr_handler *handler, | |
201 | struct mnt_idmap *idmap, | |
202 | struct dentry *dentry, struct inode *inode, | |
203 | const char *name, const void *value, | |
204 | size_t size, int flags) | |
205 | { | |
dad02fad AL |
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; | |
420a62dd AG |
218 | } |
219 | ||
220 | static int ovl_other_xattr_get(const struct xattr_handler *handler, | |
221 | struct dentry *dentry, struct inode *inode, | |
222 | const char *name, void *buffer, size_t size) | |
223 | { | |
224 | return ovl_xattr_get(dentry, inode, name, buffer, size); | |
225 | } | |
226 | ||
227 | static int ovl_other_xattr_set(const struct xattr_handler *handler, | |
228 | struct mnt_idmap *idmap, | |
229 | struct dentry *dentry, struct inode *inode, | |
230 | const char *name, const void *value, | |
231 | size_t size, int flags) | |
232 | { | |
233 | return ovl_xattr_set(dentry, inode, name, value, size, flags); | |
234 | } | |
235 | ||
236 | static const struct xattr_handler ovl_own_trusted_xattr_handler = { | |
237 | .prefix = OVL_XATTR_TRUSTED_PREFIX, | |
238 | .get = ovl_own_xattr_get, | |
239 | .set = ovl_own_xattr_set, | |
240 | }; | |
241 | ||
242 | static const struct xattr_handler ovl_own_user_xattr_handler = { | |
243 | .prefix = OVL_XATTR_USER_PREFIX, | |
244 | .get = ovl_own_xattr_get, | |
245 | .set = ovl_own_xattr_set, | |
246 | }; | |
247 | ||
248 | static const struct xattr_handler ovl_other_xattr_handler = { | |
249 | .prefix = "", /* catch all */ | |
250 | .get = ovl_other_xattr_get, | |
251 | .set = ovl_other_xattr_set, | |
252 | }; | |
253 | ||
254 | static const struct xattr_handler * const ovl_trusted_xattr_handlers[] = { | |
255 | &ovl_own_trusted_xattr_handler, | |
256 | &ovl_other_xattr_handler, | |
257 | NULL | |
258 | }; | |
259 | ||
260 | static const struct xattr_handler * const ovl_user_xattr_handlers[] = { | |
261 | &ovl_own_user_xattr_handler, | |
262 | &ovl_other_xattr_handler, | |
263 | NULL | |
264 | }; | |
265 | ||
266 | const struct xattr_handler * const *ovl_xattr_handlers(struct ovl_fs *ofs) | |
267 | { | |
268 | return ofs->config.userxattr ? ovl_user_xattr_handlers : | |
269 | ovl_trusted_xattr_handlers; | |
270 | } | |
271 |