aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-10-14 20:23:33 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2016-10-14 20:23:33 -0400
commit1a892b485f328224b4882818f84fcc0a3208677d (patch)
treec0ec8fd39c2cdcb00996ac42e7f253f8157cf41d
parent5d89d9f502f9c33ed0270d716f238429861e1942 (diff)
parent7764235becf3b72bd124400fbffe670531322135 (diff)
Merge branch 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs
Pull overlayfs updates from Miklos Szeredi: "This update contains fixes to the "use mounter's permission to access underlying layers" area, and miscellaneous other fixes and cleanups. No new features this time" * 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs: ovl: use vfs_get_link() vfs: add vfs_get_link() helper ovl: use generic_readlink ovl: explain error values when removing acl from workdir ovl: Fix info leak in ovl_lookup_temp() ovl: during copy up, switch to mounter's creds early ovl: lookup: do getxattr with mounter's permission ovl: copy_up_xattr(): use strnlen
-rw-r--r--fs/namei.c25
-rw-r--r--fs/overlayfs/copy_up.c67
-rw-r--r--fs/overlayfs/dir.c5
-rw-r--r--fs/overlayfs/inode.c44
-rw-r--r--fs/overlayfs/super.c33
-rw-r--r--include/linux/fs.h1
6 files changed, 82 insertions, 93 deletions
diff --git a/fs/namei.c b/fs/namei.c
index a7f601cd521a..5b4eed221530 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -4668,6 +4668,31 @@ int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen)
4668} 4668}
4669EXPORT_SYMBOL(generic_readlink); 4669EXPORT_SYMBOL(generic_readlink);
4670 4670
4671/**
4672 * vfs_get_link - get symlink body
4673 * @dentry: dentry on which to get symbolic link
4674 * @done: caller needs to free returned data with this
4675 *
4676 * Calls security hook and i_op->get_link() on the supplied inode.
4677 *
4678 * It does not touch atime. That's up to the caller if necessary.
4679 *
4680 * Does not work on "special" symlinks like /proc/$$/fd/N
4681 */
4682const char *vfs_get_link(struct dentry *dentry, struct delayed_call *done)
4683{
4684 const char *res = ERR_PTR(-EINVAL);
4685 struct inode *inode = d_inode(dentry);
4686
4687 if (d_is_symlink(dentry)) {
4688 res = ERR_PTR(security_inode_readlink(dentry));
4689 if (!res)
4690 res = inode->i_op->get_link(dentry, inode, done);
4691 }
4692 return res;
4693}
4694EXPORT_SYMBOL(vfs_get_link);
4695
4671/* get the link contents into pagecache */ 4696/* get the link contents into pagecache */
4672const char *page_get_link(struct dentry *dentry, struct inode *inode, 4697const char *page_get_link(struct dentry *dentry, struct inode *inode,
4673 struct delayed_call *callback) 4698 struct delayed_call *callback)
diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
index 3f803b3a1f82..aeb60f791418 100644
--- a/fs/overlayfs/copy_up.c
+++ b/fs/overlayfs/copy_up.c
@@ -57,6 +57,7 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new)
57 ssize_t list_size, size, value_size = 0; 57 ssize_t list_size, size, value_size = 0;
58 char *buf, *name, *value = NULL; 58 char *buf, *name, *value = NULL;
59 int uninitialized_var(error); 59 int uninitialized_var(error);
60 size_t slen;
60 61
61 if (!(old->d_inode->i_opflags & IOP_XATTR) || 62 if (!(old->d_inode->i_opflags & IOP_XATTR) ||
62 !(new->d_inode->i_opflags & IOP_XATTR)) 63 !(new->d_inode->i_opflags & IOP_XATTR))
@@ -79,7 +80,16 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new)
79 goto out; 80 goto out;
80 } 81 }
81 82
82 for (name = buf; name < (buf + list_size); name += strlen(name) + 1) { 83 for (name = buf; list_size; name += slen) {
84 slen = strnlen(name, list_size) + 1;
85
86 /* underlying fs providing us with an broken xattr list? */
87 if (WARN_ON(slen > list_size)) {
88 error = -EIO;
89 break;
90 }
91 list_size -= slen;
92
83 if (ovl_is_private_xattr(name)) 93 if (ovl_is_private_xattr(name))
84 continue; 94 continue;
85retry: 95retry:
@@ -174,40 +184,6 @@ out_fput:
174 return error; 184 return error;
175} 185}
176 186
177static char *ovl_read_symlink(struct dentry *realdentry)
178{
179 int res;
180 char *buf;
181 struct inode *inode = realdentry->d_inode;
182 mm_segment_t old_fs;
183
184 res = -EINVAL;
185 if (!inode->i_op->readlink)
186 goto err;
187
188 res = -ENOMEM;
189 buf = (char *) __get_free_page(GFP_KERNEL);
190 if (!buf)
191 goto err;
192
193 old_fs = get_fs();
194 set_fs(get_ds());
195 /* The cast to a user pointer is valid due to the set_fs() */
196 res = inode->i_op->readlink(realdentry,
197 (char __user *)buf, PAGE_SIZE - 1);
198 set_fs(old_fs);
199 if (res < 0) {
200 free_page((unsigned long) buf);
201 goto err;
202 }
203 buf[res] = '\0';
204
205 return buf;
206
207err:
208 return ERR_PTR(res);
209}
210
211static int ovl_set_timestamps(struct dentry *upperdentry, struct kstat *stat) 187static int ovl_set_timestamps(struct dentry *upperdentry, struct kstat *stat)
212{ 188{
213 struct iattr attr = { 189 struct iattr attr = {
@@ -354,19 +330,20 @@ out_cleanup:
354int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, 330int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
355 struct path *lowerpath, struct kstat *stat) 331 struct path *lowerpath, struct kstat *stat)
356{ 332{
333 DEFINE_DELAYED_CALL(done);
357 struct dentry *workdir = ovl_workdir(dentry); 334 struct dentry *workdir = ovl_workdir(dentry);
358 int err; 335 int err;
359 struct kstat pstat; 336 struct kstat pstat;
360 struct path parentpath; 337 struct path parentpath;
338 struct dentry *lowerdentry = lowerpath->dentry;
361 struct dentry *upperdir; 339 struct dentry *upperdir;
362 struct dentry *upperdentry; 340 struct dentry *upperdentry;
363 const struct cred *old_cred; 341 const char *link = NULL;
364 char *link = NULL;
365 342
366 if (WARN_ON(!workdir)) 343 if (WARN_ON(!workdir))
367 return -EROFS; 344 return -EROFS;
368 345
369 ovl_do_check_copy_up(lowerpath->dentry); 346 ovl_do_check_copy_up(lowerdentry);
370 347
371 ovl_path_upper(parent, &parentpath); 348 ovl_path_upper(parent, &parentpath);
372 upperdir = parentpath.dentry; 349 upperdir = parentpath.dentry;
@@ -376,13 +353,11 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
376 return err; 353 return err;
377 354
378 if (S_ISLNK(stat->mode)) { 355 if (S_ISLNK(stat->mode)) {
379 link = ovl_read_symlink(lowerpath->dentry); 356 link = vfs_get_link(lowerdentry, &done);
380 if (IS_ERR(link)) 357 if (IS_ERR(link))
381 return PTR_ERR(link); 358 return PTR_ERR(link);
382 } 359 }
383 360
384 old_cred = ovl_override_creds(dentry->d_sb);
385
386 err = -EIO; 361 err = -EIO;
387 if (lock_rename(workdir, upperdir) != NULL) { 362 if (lock_rename(workdir, upperdir) != NULL) {
388 pr_err("overlayfs: failed to lock workdir+upperdir\n"); 363 pr_err("overlayfs: failed to lock workdir+upperdir\n");
@@ -403,19 +378,16 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
403 } 378 }
404out_unlock: 379out_unlock:
405 unlock_rename(workdir, upperdir); 380 unlock_rename(workdir, upperdir);
406 revert_creds(old_cred); 381 do_delayed_call(&done);
407
408 if (link)
409 free_page((unsigned long) link);
410 382
411 return err; 383 return err;
412} 384}
413 385
414int ovl_copy_up(struct dentry *dentry) 386int ovl_copy_up(struct dentry *dentry)
415{ 387{
416 int err; 388 int err = 0;
389 const struct cred *old_cred = ovl_override_creds(dentry->d_sb);
417 390
418 err = 0;
419 while (!err) { 391 while (!err) {
420 struct dentry *next; 392 struct dentry *next;
421 struct dentry *parent; 393 struct dentry *parent;
@@ -447,6 +419,7 @@ int ovl_copy_up(struct dentry *dentry)
447 dput(parent); 419 dput(parent);
448 dput(next); 420 dput(next);
449 } 421 }
422 revert_creds(old_cred);
450 423
451 return err; 424 return err;
452} 425}
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index 5f90ddf778ba..306b6c161840 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -14,6 +14,7 @@
14#include <linux/cred.h> 14#include <linux/cred.h>
15#include <linux/posix_acl.h>