diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-14 20:23:33 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-14 20:23:33 -0400 |
| commit | 1a892b485f328224b4882818f84fcc0a3208677d (patch) | |
| tree | c0ec8fd39c2cdcb00996ac42e7f253f8157cf41d | |
| parent | 5d89d9f502f9c33ed0270d716f238429861e1942 (diff) | |
| parent | 7764235becf3b72bd124400fbffe670531322135 (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.c | 25 | ||||
| -rw-r--r-- | fs/overlayfs/copy_up.c | 67 | ||||
| -rw-r--r-- | fs/overlayfs/dir.c | 5 | ||||
| -rw-r--r-- | fs/overlayfs/inode.c | 44 | ||||
| -rw-r--r-- | fs/overlayfs/super.c | 33 | ||||
| -rw-r--r-- | include/linux/fs.h | 1 |
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 | } |
| 4669 | EXPORT_SYMBOL(generic_readlink); | 4669 | EXPORT_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 | */ | ||
| 4682 | const 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 | } | ||
| 4694 | EXPORT_SYMBOL(vfs_get_link); | ||
| 4695 | |||
| 4671 | /* get the link contents into pagecache */ | 4696 | /* get the link contents into pagecache */ |
| 4672 | const char *page_get_link(struct dentry *dentry, struct inode *inode, | 4697 | const 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; |
| 85 | retry: | 95 | retry: |
| @@ -174,40 +184,6 @@ out_fput: | |||
| 174 | return error; | 184 | return error; |
| 175 | } | 185 | } |
| 176 | 186 | ||
| 177 | static 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 | |||
| 207 | err: | ||
| 208 | return ERR_PTR(res); | ||
| 209 | } | ||
| 210 | |||
| 211 | static int ovl_set_timestamps(struct dentry *upperdentry, struct kstat *stat) | 187 | static 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: | |||
| 354 | int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, | 330 | int 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 | } |
| 404 | out_unlock: | 379 | out_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 | ||
| 414 | int ovl_copy_up(struct dentry *dentry) | 386 | int 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> | ||
