diff options
Diffstat (limited to 'fs/overlayfs/inode.c')
-rw-r--r-- | fs/overlayfs/inode.c | 425 |
1 files changed, 425 insertions, 0 deletions
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c new file mode 100644 index 000000000000..af2d18c9fcee --- /dev/null +++ b/fs/overlayfs/inode.c | |||
@@ -0,0 +1,425 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Copyright (C) 2011 Novell Inc. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License version 2 as published by | ||
7 | * the Free Software Foundation. | ||
8 | */ | ||
9 | |||
10 | #include <linux/fs.h> | ||
11 | #include <linux/slab.h> | ||
12 | #include <linux/xattr.h> | ||
13 | #include "overlayfs.h" | ||
14 | |||
15 | static int ovl_copy_up_last(struct dentry *dentry, struct iattr *attr, | ||
16 | bool no_data) | ||
17 | { | ||
18 | int err; | ||
19 | struct dentry *parent; | ||
20 | struct kstat stat; | ||
21 | struct path lowerpath; | ||
22 | |||
23 | parent = dget_parent(dentry); | ||
24 | err = ovl_copy_up(parent); | ||
25 | if (err) | ||
26 | goto out_dput_parent; | ||
27 | |||
28 | ovl_path_lower(dentry, &lowerpath); | ||
29 | err = vfs_getattr(&lowerpath, &stat); | ||
30 | if (err) | ||
31 | goto out_dput_parent; | ||
32 | |||
33 | if (no_data) | ||
34 | stat.size = 0; | ||
35 | |||
36 | err = ovl_copy_up_one(parent, dentry, &lowerpath, &stat, attr); | ||
37 | |||
38 | out_dput_parent: | ||
39 | dput(parent); | ||
40 | return err; | ||
41 | } | ||
42 | |||
43 | int ovl_setattr(struct dentry *dentry, struct iattr *attr) | ||
44 | { | ||
45 | int err; | ||
46 | struct dentry *upperdentry; | ||
47 | |||
48 | err = ovl_want_write(dentry); | ||
49 | if (err) | ||
50 | goto out; | ||
51 | |||
52 | upperdentry = ovl_dentry_upper(dentry); | ||
53 | if (upperdentry) { | ||
54 | mutex_lock(&upperdentry->d_inode->i_mutex); | ||
55 | err = notify_change(upperdentry, attr, NULL); | ||
56 | mutex_unlock(&upperdentry->d_inode->i_mutex); | ||
57 | } else { | ||
58 | err = ovl_copy_up_last(dentry, attr, false); | ||
59 | } | ||
60 | ovl_drop_write(dentry); | ||
61 | out: | ||
62 | return err; | ||
63 | } | ||
64 | |||
65 | static int ovl_getattr(struct vfsmount *mnt, struct dentry *dentry, | ||
66 | struct kstat *stat) | ||
67 | { | ||
68 | struct path realpath; | ||
69 | |||
70 | ovl_path_real(dentry, &realpath); | ||
71 | return vfs_getattr(&realpath, stat); | ||
72 | } | ||
73 | |||
74 | int ovl_permission(struct inode *inode, int mask) | ||
75 | { | ||
76 | struct ovl_entry *oe; | ||
77 | struct dentry *alias = NULL; | ||
78 | struct inode *realinode; | ||
79 | struct dentry *realdentry; | ||
80 | bool is_upper; | ||
81 | int err; | ||
82 | |||
83 | if (S_ISDIR(inode->i_mode)) { | ||
84 | oe = inode->i_private; | ||
85 | } else if (mask & MAY_NOT_BLOCK) { | ||
86 | return -ECHILD; | ||
87 | } else { | ||
88 | /* | ||
89 | * For non-directories find an alias and get the info | ||
90 | * from there. | ||
91 | */ | ||
92 | alias = d_find_any_alias(inode); | ||
93 | if (WARN_ON(!alias)) | ||
94 | return -ENOENT; | ||
95 | |||
96 | oe = alias->d_fsdata; | ||
97 | } | ||
98 | |||
99 | realdentry = ovl_entry_real(oe, &is_upper); | ||
100 | |||
101 | /* Careful in RCU walk mode */ | ||
102 | realinode = ACCESS_ONCE(realdentry->d_inode); | ||
103 | if (!realinode) { | ||
104 | WARN_ON(!(mask & MAY_NOT_BLOCK)); | ||
105 | err = -ENOENT; | ||
106 | goto out_dput; | ||
107 | } | ||
108 | |||
109 | if (mask & MAY_WRITE) { | ||
110 | umode_t mode = realinode->i_mode; | ||
111 | |||
112 | /* | ||
113 | * Writes will always be redirected to upper layer, so | ||
114 | * ignore lower layer being read-only. | ||
115 | * | ||
116 | * If the overlay itself is read-only then proceed | ||
117 | * with the permission check, don't return EROFS. | ||
118 | * This will only happen if this is the lower layer of | ||
119 | * another overlayfs. | ||
120 | * | ||
121 | * If upper fs becomes read-only after the overlay was | ||
122 | * constructed return EROFS to prevent modification of | ||
123 | * upper layer. | ||
124 | */ | ||
125 | err = -EROFS; | ||
126 | if (is_upper && !IS_RDONLY(inode) && IS_RDONLY(realinode) && | ||
127 | (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) | ||
128 | goto out_dput; | ||
129 | } | ||
130 | |||
131 | err = __inode_permission(realinode, mask); | ||
132 | out_dput: | ||
133 | dput(alias); | ||
134 | return err; | ||
135 | } | ||
136 | |||
137 | |||
138 | struct ovl_link_data { | ||
139 | struct dentry *realdentry; | ||
140 | void *cookie; | ||
141 | }; | ||
142 | |||
143 | static void *ovl_follow_link(struct dentry *dentry, struct nameidata *nd) | ||
144 | { | ||
145 | void *ret; | ||
146 | struct dentry *realdentry; | ||
147 | struct inode *realinode; | ||
148 | |||
149 | realdentry = ovl_dentry_real(dentry); | ||
150 | realinode = realdentry->d_inode; | ||
151 | |||
152 | if (WARN_ON(!realinode->i_op->follow_link)) | ||
153 | return ERR_PTR(-EPERM); | ||
154 | |||
155 | ret = realinode->i_op->follow_link(realdentry, nd); | ||
156 | if (IS_ERR(ret)) | ||
157 | return ret; | ||
158 | |||
159 | if (realinode->i_op->put_link) { | ||
160 | struct ovl_link_data *data; | ||
161 | |||
162 | data = kmalloc(sizeof(struct ovl_link_data), GFP_KERNEL); | ||
163 | if (!data) { | ||
164 | realinode->i_op->put_link(realdentry, nd, ret); | ||
165 | return ERR_PTR(-ENOMEM); | ||
166 | } | ||
167 | data->realdentry = realdentry; | ||
168 | data->cookie = ret; | ||
169 | |||
170 | return data; | ||
171 | } else { | ||
172 | return NULL; | ||
173 | } | ||
174 | } | ||
175 | |||
176 | static void ovl_put_link(struct dentry *dentry, struct nameidata *nd, void *c) | ||
177 | { | ||
178 | struct inode *realinode; | ||
179 | struct ovl_link_data *data = c; | ||
180 | |||
181 | if (!data) | ||
182 | return; | ||
183 | |||
184 | realinode = data->realdentry->d_inode; | ||
185 | realinode->i_op->put_link(data->realdentry, nd, data->cookie); | ||
186 | kfree(data); | ||
187 | } | ||
188 | |||
189 | static int ovl_readlink(struct dentry *dentry, char __user *buf, int bufsiz) | ||
190 | { | ||
191 | struct path realpath; | ||
192 | struct inode *realinode; | ||
193 | |||
194 | ovl_path_real(dentry, &realpath); | ||
195 | realinode = realpath.dentry->d_inode; | ||
196 | |||
197 | if (!realinode->i_op->readlink) | ||
198 | return -EINVAL; | ||
199 | |||
200 | touch_atime(&realpath); | ||
201 | |||
202 | return realinode->i_op->readlink(realpath.dentry, buf, bufsiz); | ||
203 | } | ||
204 | |||
205 | |||
206 | static bool ovl_is_private_xattr(const char *name) | ||
207 | { | ||
208 | return strncmp(name, "trusted.overlay.", 14) == 0; | ||
209 | } | ||
210 | |||
211 | int ovl_setxattr(struct dentry *dentry, const char *name, | ||
212 | const void *value, size_t size, int flags) | ||
213 | { | ||
214 | int err; | ||
215 | struct dentry *upperdentry; | ||
216 | |||
217 | err = ovl_want_write(dentry); | ||
218 | if (err) | ||
219 | goto out; | ||
220 | |||
221 | err = -EPERM; | ||
222 | if (ovl_is_private_xattr(name)) | ||
223 | goto out_drop_write; | ||
224 | |||
225 | err = ovl_copy_up(dentry); | ||
226 | if (err) | ||
227 | goto out_drop_write; | ||
228 | |||
229 | upperdentry = ovl_dentry_upper(dentry); | ||
230 | err = vfs_setxattr(upperdentry, name, value, size, flags); | ||
231 | |||
232 | out_drop_write: | ||
233 | ovl_drop_write(dentry); | ||
234 | out: | ||
235 | return err; | ||
236 | } | ||
237 | |||
238 | ssize_t ovl_getxattr(struct dentry *dentry, const char *name, | ||
239 | void *value, size_t size) | ||
240 | { | ||
241 | if (ovl_path_type(dentry->d_parent) == OVL_PATH_MERGE && | ||
242 | ovl_is_private_xattr(name)) | ||
243 | return -ENODATA; | ||
244 | |||
245 | return vfs_getxattr(ovl_dentry_real(dentry), name, value, size); | ||
246 | } | ||
247 | |||
248 | ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size) | ||
249 | { | ||
250 | ssize_t res; | ||
251 | int off; | ||
252 | |||
253 | res = vfs_listxattr(ovl_dentry_real(dentry), list, size); | ||
254 | if (res <= 0 || size == 0) | ||
255 | return res; | ||
256 | |||
257 | if (ovl_path_type(dentry->d_parent) != OVL_PATH_MERGE) | ||
258 | return res; | ||
259 | |||
260 | /* filter out private xattrs */ | ||
261 | for (off = 0; off < res;) { | ||
262 | char *s = list + off; | ||
263 | size_t slen = strlen(s) + 1; | ||
264 | |||
265 | BUG_ON(off + slen > res); | ||
266 | |||
267 | if (ovl_is_private_xattr(s)) { | ||
268 | res -= slen; | ||
269 | memmove(s, s + slen, res - off); | ||
270 | } else { | ||
271 | off += slen; | ||
272 | } | ||
273 | } | ||
274 | |||
275 | return res; | ||
276 | } | ||
277 | |||
278 | int ovl_removexattr(struct dentry *dentry, const char *name) | ||
279 | { | ||
280 | int err; | ||
281 | struct path realpath; | ||
282 | enum ovl_path_type type; | ||
283 | |||
284 | err = ovl_want_write(dentry); | ||
285 | if (err) | ||
286 | goto out; | ||
287 | |||
288 | if (ovl_path_type(dentry->d_parent) == OVL_PATH_MERGE && | ||
289 | ovl_is_private_xattr(name)) | ||
290 | goto out_drop_write; | ||
291 | |||
292 | type = ovl_path_real(dentry, &realpath); | ||
293 | if (type == OVL_PATH_LOWER) { | ||
294 | err = vfs_getxattr(realpath.dentry, name, NULL, 0); | ||
295 | if (err < 0) | ||
296 | goto out_drop_write; | ||
297 | |||
298 | err = ovl_copy_up(dentry); | ||
299 | if (err) | ||
300 | goto out_drop_write; | ||
301 | |||
302 | ovl_path_upper(dentry, &realpath); | ||
303 | } | ||
304 | |||
305 | err = vfs_removexattr(realpath.dentry, name); | ||
306 | out_drop_write: | ||
307 | ovl_drop_write(dentry); | ||
308 | out: | ||
309 | return err; | ||
310 | } | ||
311 | |||
312 | static bool ovl_open_need_copy_up(int flags, enum ovl_path_type type, | ||
313 | struct dentry *realdentry) | ||
314 | { | ||
315 | if (type != OVL_PATH_LOWER) | ||
316 | return false; | ||
317 | |||
318 | if (special_file(realdentry->d_inode->i_mode)) | ||
319 | return false; | ||
320 | |||
321 | if (!(OPEN_FMODE(flags) & FMODE_WRITE) && !(flags & O_TRUNC)) | ||
322 | return false; | ||
323 | |||
324 | return true; | ||
325 | } | ||
326 | |||
327 | static int ovl_dentry_open(struct dentry *dentry, struct file *file, | ||
328 | const struct cred *cred) | ||
329 | { | ||
330 | int err; | ||
331 | struct path realpath; | ||
332 | enum ovl_path_type type; | ||
333 | bool want_write = false; | ||
334 | |||
335 | type = ovl_path_real(dentry, &realpath); | ||
336 | if (ovl_open_need_copy_up(file->f_flags, type, realpath.dentry)) { | ||
337 | want_write = true; | ||
338 | err = ovl_want_write(dentry); | ||
339 | if (err) | ||
340 | goto out; | ||
341 | |||
342 | if (file->f_flags & O_TRUNC) | ||
343 | err = ovl_copy_up_last(dentry, NULL, true); | ||
344 | else | ||
345 | err = ovl_copy_up(dentry); | ||
346 | if (err) | ||
347 | goto out_drop_write; | ||
348 | |||
349 | ovl_path_upper(dentry, &realpath); | ||
350 | } | ||
351 | |||
352 | err = vfs_open(&realpath, file, cred); | ||
353 | out_drop_write: | ||
354 | if (want_write) | ||
355 | ovl_drop_write(dentry); | ||
356 | out: | ||
357 | return err; | ||
358 | } | ||
359 | |||
360 | static const struct inode_operations ovl_file_inode_operations = { | ||
361 | .setattr = ovl_setattr, | ||
362 | .permission = ovl_permission, | ||
363 | .getattr = ovl_getattr, | ||
364 | .setxattr = ovl_setxattr, | ||
365 | .getxattr = ovl_getxattr, | ||
366 | .listxattr = ovl_listxattr, | ||
367 | .removexattr = ovl_removexattr, | ||
368 | .dentry_open = ovl_dentry_open, | ||
369 | }; | ||
370 | |||
371 | static const struct inode_operations ovl_symlink_inode_operations = { | ||
372 | .setattr = ovl_setattr, | ||
373 | .follow_link = ovl_follow_link, | ||
374 | .put_link = ovl_put_link, | ||
375 | .readlink = ovl_readlink, | ||
376 | .getattr = ovl_getattr, | ||
377 | .setxattr = ovl_setxattr, | ||
378 | .getxattr = ovl_getxattr, | ||
379 | .listxattr = ovl_listxattr, | ||
380 | .removexattr = ovl_removexattr, | ||
381 | }; | ||
382 | |||
383 | struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, | ||
384 | struct ovl_entry *oe) | ||
385 | { | ||
386 | struct inode *inode; | ||
387 | |||
388 | inode = new_inode(sb); | ||
389 | if (!inode) | ||
390 | return NULL; | ||
391 | |||
392 | mode &= S_IFMT; | ||
393 | |||
394 | inode->i_ino = get_next_ino(); | ||
395 | inode->i_mode = mode; | ||
396 | inode->i_flags |= S_NOATIME | S_NOCMTIME; | ||
397 | |||
398 | switch (mode) { | ||
399 | case S_IFDIR: | ||
400 | inode->i_private = oe; | ||
401 | inode->i_op = &ovl_dir_inode_operations; | ||
402 | inode->i_fop = &ovl_dir_operations; | ||
403 | break; | ||
404 | |||
405 | case S_IFLNK: | ||
406 | inode->i_op = &ovl_symlink_inode_operations; | ||
407 | break; | ||
408 | |||
409 | case S_IFREG: | ||
410 | case S_IFSOCK: | ||
411 | case S_IFBLK: | ||
412 | case S_IFCHR: | ||
413 | case S_IFIFO: | ||
414 | inode->i_op = &ovl_file_inode_operations; | ||
415 | break; | ||
416 | |||
417 | default: | ||
418 | WARN(1, "illegal file type: %i\n", mode); | ||
419 | iput(inode); | ||
420 | inode = NULL; | ||
421 | } | ||
422 | |||
423 | return inode; | ||
424 | |||
425 | } | ||