diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-10-06 11:52:53 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-10-06 11:52:53 -0400 |
commit | 8d4ef4e15ec57eec5d24fb109fbc220eea9f0caf (patch) | |
tree | a3e047f8f3670b06a367a211d1bb5f1785302736 | |
parent | 1249b571ba2a44a929f3053fd0a90b36a8535c55 (diff) | |
parent | 85fdee1eef1a9e48ad5716916677e0c5fbc781e3 (diff) |
Merge branch 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs
Pull overlayfs fixes from Miklos Szeredi:
"Fix a regression in 4.14 and one in 4.13. The latter is a case when
Docker is doing something it really shouldn't and gets away with it.
We now print a warning instead of erroring out.
There are also fixes to several error paths"
* 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs:
ovl: fix regression caused by exclusive upper/work dir protection
ovl: fix missing unlock_rename() in ovl_do_copy_up()
ovl: fix dentry leak in ovl_indexdir_cleanup()
ovl: fix dput() of ERR_PTR in ovl_cleanup_index()
ovl: fix error value printed in ovl_lookup_index()
ovl: fix may_write_real() for overlayfs directories
-rw-r--r-- | Documentation/filesystems/overlayfs.txt | 5 | ||||
-rw-r--r-- | fs/namespace.c | 4 | ||||
-rw-r--r-- | fs/overlayfs/copy_up.c | 6 | ||||
-rw-r--r-- | fs/overlayfs/dir.c | 20 | ||||
-rw-r--r-- | fs/overlayfs/namei.c | 1 | ||||
-rw-r--r-- | fs/overlayfs/overlayfs.h | 1 | ||||
-rw-r--r-- | fs/overlayfs/ovl_entry.h | 3 | ||||
-rw-r--r-- | fs/overlayfs/readdir.c | 6 | ||||
-rw-r--r-- | fs/overlayfs/super.c | 27 | ||||
-rw-r--r-- | fs/overlayfs/util.c | 24 |
10 files changed, 60 insertions, 37 deletions
diff --git a/Documentation/filesystems/overlayfs.txt b/Documentation/filesystems/overlayfs.txt index 36f528a7fdd6..8caa60734647 100644 --- a/Documentation/filesystems/overlayfs.txt +++ b/Documentation/filesystems/overlayfs.txt | |||
@@ -210,8 +210,11 @@ path as another overlay mount and it may use a lower layer path that is | |||
210 | beneath or above the path of another overlay lower layer path. | 210 | beneath or above the path of another overlay lower layer path. |
211 | 211 | ||
212 | Using an upper layer path and/or a workdir path that are already used by | 212 | Using an upper layer path and/or a workdir path that are already used by |
213 | another overlay mount is not allowed and will fail with EBUSY. Using | 213 | another overlay mount is not allowed and may fail with EBUSY. Using |
214 | partially overlapping paths is not allowed but will not fail with EBUSY. | 214 | partially overlapping paths is not allowed but will not fail with EBUSY. |
215 | If files are accessed from two overlayfs mounts which share or overlap the | ||
216 | upper layer and/or workdir path the behavior of the overlay is undefined, | ||
217 | though it will not result in a crash or deadlock. | ||
215 | 218 | ||
216 | Mounting an overlay using an upper layer path, where the upper layer path | 219 | Mounting an overlay using an upper layer path, where the upper layer path |
217 | was previously used by another mounted overlay in combination with a | 220 | was previously used by another mounted overlay in combination with a |
diff --git a/fs/namespace.c b/fs/namespace.c index 54059b142d6b..3b601f115b6c 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -468,7 +468,9 @@ static inline int may_write_real(struct file *file) | |||
468 | 468 | ||
469 | /* File refers to upper, writable layer? */ | 469 | /* File refers to upper, writable layer? */ |
470 | upperdentry = d_real(dentry, NULL, 0, D_REAL_UPPER); | 470 | upperdentry = d_real(dentry, NULL, 0, D_REAL_UPPER); |
471 | if (upperdentry && file_inode(file) == d_inode(upperdentry)) | 471 | if (upperdentry && |
472 | (file_inode(file) == d_inode(upperdentry) || | ||
473 | file_inode(file) == d_inode(dentry))) | ||
472 | return 0; | 474 | return 0; |
473 | 475 | ||
474 | /* Lower layer: can't write to real file, sorry... */ | 476 | /* Lower layer: can't write to real file, sorry... */ |
diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index aad97b30d5e6..c441f9387a1b 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c | |||
@@ -561,10 +561,8 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c) | |||
561 | c->tmpfile = true; | 561 | c->tmpfile = true; |
562 | err = ovl_copy_up_locked(c); | 562 | err = ovl_copy_up_locked(c); |
563 | } else { | 563 | } else { |
564 | err = -EIO; | 564 | err = ovl_lock_rename_workdir(c->workdir, c->destdir); |
565 | if (lock_rename(c->workdir, c->destdir) != NULL) { | 565 | if (!err) { |
566 | pr_err("overlayfs: failed to lock workdir+upperdir\n"); | ||
567 | } else { | ||
568 | err = ovl_copy_up_locked(c); | 566 | err = ovl_copy_up_locked(c); |
569 | unlock_rename(c->workdir, c->destdir); | 567 | unlock_rename(c->workdir, c->destdir); |
570 | } | 568 | } |
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 3309b1912241..cc961a3bd3bd 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c | |||
@@ -216,26 +216,6 @@ out_unlock: | |||
216 | return err; | 216 | return err; |
217 | } | 217 | } |
218 | 218 | ||
219 | static int ovl_lock_rename_workdir(struct dentry *workdir, | ||
220 | struct dentry *upperdir) | ||
221 | { | ||
222 | /* Workdir should not be the same as upperdir */ | ||
223 | if (workdir == upperdir) | ||
224 | goto err; | ||
225 | |||
226 | /* Workdir should not be subdir of upperdir and vice versa */ | ||
227 | if (lock_rename(workdir, upperdir) != NULL) | ||
228 | goto err_unlock; | ||
229 | |||
230 | return 0; | ||
231 | |||
232 | err_unlock: | ||
233 | unlock_rename(workdir, upperdir); | ||
234 | err: | ||
235 | pr_err("overlayfs: failed to lock workdir+upperdir\n"); | ||
236 | return -EIO; | ||
237 | } | ||
238 | |||
239 | static struct dentry *ovl_clear_empty(struct dentry *dentry, | 219 | static struct dentry *ovl_clear_empty(struct dentry *dentry, |
240 | struct list_head *list) | 220 | struct list_head *list) |
241 | { | 221 | { |
diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index c3addd1114f1..654bea1a5ac9 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c | |||
@@ -506,6 +506,7 @@ static struct dentry *ovl_lookup_index(struct dentry *dentry, | |||
506 | 506 | ||
507 | index = lookup_one_len_unlocked(name.name, ofs->indexdir, name.len); | 507 | index = lookup_one_len_unlocked(name.name, ofs->indexdir, name.len); |
508 | if (IS_ERR(index)) { | 508 | if (IS_ERR(index)) { |
509 | err = PTR_ERR(index); | ||
509 | pr_warn_ratelimited("overlayfs: failed inode index lookup (ino=%lu, key=%*s, err=%i);\n" | 510 | pr_warn_ratelimited("overlayfs: failed inode index lookup (ino=%lu, key=%*s, err=%i);\n" |
510 | "overlayfs: mount with '-o index=off' to disable inodes index.\n", | 511 | "overlayfs: mount with '-o index=off' to disable inodes index.\n", |
511 | d_inode(origin)->i_ino, name.len, name.name, | 512 | d_inode(origin)->i_ino, name.len, name.name, |
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index d4e8c1a08fb0..c706a6f99928 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h | |||
@@ -235,6 +235,7 @@ bool ovl_inuse_trylock(struct dentry *dentry); | |||
235 | void ovl_inuse_unlock(struct dentry *dentry); | 235 | void ovl_inuse_unlock(struct dentry *dentry); |
236 | int ovl_nlink_start(struct dentry *dentry, bool *locked); | 236 | int ovl_nlink_start(struct dentry *dentry, bool *locked); |
237 | void ovl_nlink_end(struct dentry *dentry, bool locked); | 237 | void ovl_nlink_end(struct dentry *dentry, bool locked); |
238 | int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir); | ||
238 | 239 | ||
239 | static inline bool ovl_is_impuredir(struct dentry *dentry) | 240 | static inline bool ovl_is_impuredir(struct dentry *dentry) |
240 | { | 241 | { |
diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h index 878a750986dd..25d9b5adcd42 100644 --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h | |||
@@ -37,6 +37,9 @@ struct ovl_fs { | |||
37 | bool noxattr; | 37 | bool noxattr; |
38 | /* sb common to all layers */ | 38 | /* sb common to all layers */ |
39 | struct super_block *same_sb; | 39 | struct super_block *same_sb; |
40 | /* Did we take the inuse lock? */ | ||
41 | bool upperdir_locked; | ||
42 | bool workdir_locked; | ||
40 | }; | 43 | }; |
41 | 44 | ||
42 | /* private information held for every overlayfs dentry */ | 45 | /* private information held for every overlayfs dentry */ |
diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index 62e9b22a2077..0f85ee9c3268 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c | |||
@@ -988,6 +988,7 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt, | |||
988 | struct path *lowerstack, unsigned int numlower) | 988 | struct path *lowerstack, unsigned int numlower) |
989 | { | 989 | { |
990 | int err; | 990 | int err; |
991 | struct dentry *index = NULL; | ||
991 | struct inode *dir = dentry->d_inode; | 992 | struct inode *dir = dentry->d_inode; |
992 | struct path path = { .mnt = mnt, .dentry = dentry }; | 993 | struct path path = { .mnt = mnt, .dentry = dentry }; |
993 | LIST_HEAD(list); | 994 | LIST_HEAD(list); |
@@ -1007,8 +1008,6 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt, | |||
1007 | 1008 | ||
1008 | inode_lock_nested(dir, I_MUTEX_PARENT); | 1009 | inode_lock_nested(dir, I_MUTEX_PARENT); |
1009 | list_for_each_entry(p, &list, l_node) { | 1010 | list_for_each_entry(p, &list, l_node) { |
1010 | struct dentry *index; | ||
1011 | |||
1012 | if (p->name[0] == '.') { | 1011 | if (p->name[0] == '.') { |
1013 | if (p->len == 1) | 1012 | if (p->len == 1) |
1014 | continue; | 1013 | continue; |
@@ -1018,6 +1017,7 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt, | |||
1018 | index = lookup_one_len(p->name, dentry, p->len); | 1017 | index = lookup_one_len(p->name, dentry, p->len); |
1019 | if (IS_ERR(index)) { | 1018 | if (IS_ERR(index)) { |
1020 | err = PTR_ERR(index); | 1019 | err = PTR_ERR(index); |
1020 | index = NULL; | ||
1021 | break; | 1021 | break; |
1022 | } | 1022 | } |
1023 | err = ovl_verify_index(index, lowerstack, numlower); | 1023 | err = ovl_verify_index(index, lowerstack, numlower); |
@@ -1029,7 +1029,9 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt, | |||
1029 | break; | 1029 | break; |
1030 | } | 1030 | } |
1031 | dput(index); | 1031 | dput(index); |
1032 | index = NULL; | ||
1032 | } | 1033 | } |
1034 | dput(index); | ||
1033 | inode_unlock(dir); | 1035 | inode_unlock(dir); |
1034 | out: | 1036 | out: |
1035 | ovl_cache_free(&list); | 1037 | ovl_cache_free(&list); |
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index fd5ea4facc62..092d150643c1 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c | |||
@@ -211,9 +211,10 @@ static void ovl_put_super(struct super_block *sb) | |||
211 | 211 | ||
212 | dput(ufs->indexdir); | 212 | dput(ufs->indexdir); |
213 | dput(ufs->workdir); | 213 | dput(ufs->workdir); |
214 | ovl_inuse_unlock(ufs->workbasedir); | 214 | if (ufs->workdir_locked) |
215 | ovl_inuse_unlock(ufs->workbasedir); | ||
215 | dput(ufs->workbasedir); | 216 | dput(ufs->workbasedir); |
216 | if (ufs->upper_mnt) | 217 | if (ufs->upper_mnt && ufs->upperdir_locked) |
217 | ovl_inuse_unlock(ufs->upper_mnt->mnt_root); | 218 | ovl_inuse_unlock(ufs->upper_mnt->mnt_root); |
218 | mntput(ufs->upper_mnt); | 219 | mntput(ufs->upper_mnt); |
219 | for (i = 0; i < ufs->numlower; i++) | 220 | for (i = 0; i < ufs->numlower; i++) |
@@ -881,9 +882,13 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) | |||
881 | goto out_put_upperpath; | 882 | goto out_put_upperpath; |
882 | 883 | ||
883 | err = -EBUSY; | 884 | err = -EBUSY; |
884 | if (!ovl_inuse_trylock(upperpath.dentry)) { | 885 | if (ovl_inuse_trylock(upperpath.dentry)) { |
885 | pr_err("overlayfs: upperdir is in-use by another mount\n"); | 886 | ufs->upperdir_locked = true; |
887 | } else if (ufs->config.index) { | ||
888 | pr_err("overlayfs: upperdir is in-use by another mount, mount with '-o index=off' to override exclusive upperdir protection.\n"); | ||
886 | goto out_put_upperpath; | 889 | goto out_put_upperpath; |
890 | } else { | ||
891 | pr_warn("overlayfs: upperdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n"); | ||
887 | } | 892 | } |
888 | 893 | ||
889 | err = ovl_mount_dir(ufs->config.workdir, &workpath); | 894 | err = ovl_mount_dir(ufs->config.workdir, &workpath); |
@@ -901,9 +906,13 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) | |||
901 | } | 906 | } |
902 | 907 | ||
903 | err = -EBUSY; | 908 | err = -EBUSY; |
904 | if (!ovl_inuse_trylock(workpath.dentry)) { | 909 | if (ovl_inuse_trylock(workpath.dentry)) { |
905 | pr_err("overlayfs: workdir is in-use by another mount\n"); | 910 | ufs->workdir_locked = true; |
911 | } else if (ufs->config.index) { | ||
912 | pr_err("overlayfs: workdir is in-use by another mount, mount with '-o index=off' to override exclusive workdir protection.\n"); | ||
906 | goto out_put_workpath; | 913 | goto out_put_workpath; |
914 | } else { | ||
915 | pr_warn("overlayfs: workdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n"); | ||
907 | } | 916 | } |
908 | 917 | ||
909 | ufs->workbasedir = workpath.dentry; | 918 | ufs->workbasedir = workpath.dentry; |
@@ -1156,11 +1165,13 @@ out_put_lowerpath: | |||
1156 | out_free_lowertmp: | 1165 | out_free_lowertmp: |
1157 | kfree(lowertmp); | 1166 | kfree(lowertmp); |
1158 | out_unlock_workdentry: | 1167 | out_unlock_workdentry: |
1159 | ovl_inuse_unlock(workpath.dentry); | 1168 | if (ufs->workdir_locked) |
1169 | ovl_inuse_unlock(workpath.dentry); | ||
1160 | out_put_workpath: | 1170 | out_put_workpath: |
1161 | path_put(&workpath); | 1171 | path_put(&workpath); |
1162 | out_unlock_upperdentry: | 1172 | out_unlock_upperdentry: |
1163 | ovl_inuse_unlock(upperpath.dentry); | 1173 | if (ufs->upperdir_locked) |
1174 | ovl_inuse_unlock(upperpath.dentry); | ||
1164 | out_put_upperpath: | 1175 | out_put_upperpath: |
1165 | path_put(&upperpath); | 1176 | path_put(&upperpath); |
1166 | out_free_config: | 1177 | out_free_config: |
diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 117794582f9f..b9b239fa5cfd 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c | |||
@@ -430,7 +430,7 @@ void ovl_inuse_unlock(struct dentry *dentry) | |||
430 | } | 430 | } |
431 | } | 431 | } |
432 | 432 | ||
433 | /* Called must hold OVL_I(inode)->oi_lock */ | 433 | /* Caller must hold OVL_I(inode)->lock */ |
434 | static void ovl_cleanup_index(struct dentry *dentry) | 434 | static void ovl_cleanup_index(struct dentry *dentry) |
435 | { | 435 | { |
436 | struct inode *dir = ovl_indexdir(dentry->d_sb)->d_inode; | 436 | struct inode *dir = ovl_indexdir(dentry->d_sb)->d_inode; |
@@ -469,6 +469,9 @@ static void ovl_cleanup_index(struct dentry *dentry) | |||
469 | err = PTR_ERR(index); | 469 | err = PTR_ERR(index); |
470 | if (!IS_ERR(index)) | 470 | if (!IS_ERR(index)) |
471 | err = ovl_cleanup(dir, index); | 471 | err = ovl_cleanup(dir, index); |
472 | else | ||
473 | index = NULL; | ||
474 | |||
472 | inode_unlock(dir); | 475 | inode_unlock(dir); |
473 | if (err) | 476 | if (err) |
474 | goto fail; | 477 | goto fail; |
@@ -557,3 +560,22 @@ void ovl_nlink_end(struct dentry *dentry, bool locked) | |||
557 | mutex_unlock(&OVL_I(d_inode(dentry))->lock); | 560 | mutex_unlock(&OVL_I(d_inode(dentry))->lock); |
558 | } | 561 | } |
559 | } | 562 | } |
563 | |||
564 | int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir) | ||
565 | { | ||
566 | /* Workdir should not be the same as upperdir */ | ||
567 | if (workdir == upperdir) | ||
568 | goto err; | ||
569 | |||
570 | /* Workdir should not be subdir of upperdir and vice versa */ | ||
571 | if (lock_rename(workdir, upperdir) != NULL) | ||
572 | goto err_unlock; | ||
573 | |||
574 | return 0; | ||
575 | |||
576 | err_unlock: | ||
577 | unlock_rename(workdir, upperdir); | ||
578 | err: | ||
579 | pr_err("overlayfs: failed to lock workdir+upperdir\n"); | ||
580 | return -EIO; | ||
581 | } | ||