diff options
author | Amir Goldstein <amir73il@gmail.com> | 2017-06-25 15:37:17 +0200 |
---|---|---|
committer | Miklos Szeredi <mszeredi@redhat.com> | 2017-11-09 10:23:26 +0100 |
commit | b79e05aaa166755fafbf02db275175edb5175df8 (patch) | |
tree | aaa5fcc30c2ffe7c22df0074900846917332c08b /fs/overlayfs/readdir.c | |
parent | ovl: lockdep annotate of nested OVL_I(inode)->lock (diff) | |
download | linux-b79e05aaa166755fafbf02db275175edb5175df8.tar.xz linux-b79e05aaa166755fafbf02db275175edb5175df8.zip |
ovl: no direct iteration for dir with origin xattr
If a non-merge dir in an overlay mount has an overlay.origin xattr, it
means it was once an upper merge dir, which may contain whiteouts and
then the lower dir was removed under it.
Do not iterate real dir directly in this case to avoid exposing whiteouts.
[SzM] Set OVL_WHITEOUT for all merge directories as well.
[amir] A directory that was just copied up does not have the OVL_WHITEOUTS
flag. We need to set it to fix merge dir iteration.
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Diffstat (limited to 'fs/overlayfs/readdir.c')
-rw-r--r-- | fs/overlayfs/readdir.c | 24 |
1 files changed, 20 insertions, 4 deletions
diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index 698b74dd750e..95d12755f847 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -316,21 +316,37 @@ static inline int ovl_dir_read(struct path *realpath, return err; } +/* + * Can we iterate real dir directly? + * + * Non-merge dir may contain whiteouts from a time it was a merge upper, before + * lower dir was removed under it and possibly before it was rotated from upper + * to lower layer. + */ +static bool ovl_dir_is_real(struct dentry *dir) +{ + return !ovl_test_flag(OVL_WHITEOUTS, d_inode(dir)); +} + static void ovl_dir_reset(struct file *file) { struct ovl_dir_file *od = file->private_data; struct ovl_dir_cache *cache = od->cache; struct dentry *dentry = file->f_path.dentry; - enum ovl_path_type type = ovl_path_type(dentry); + bool is_real; if (cache && ovl_dentry_version_get(dentry) != cache->version) { ovl_cache_put(od, dentry); od->cache = NULL; od->cursor = NULL; } - WARN_ON(!od->is_real && !OVL_TYPE_MERGE(type)); - if (od->is_real && OVL_TYPE_MERGE(type)) + is_real = ovl_dir_is_real(dentry); + if (od->is_real != is_real) { + /* is_real can only become false when dir is copied up */ + if (WARN_ON(is_real)) + return; od->is_real = false; + } } static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list, @@ -816,7 +832,7 @@ static int ovl_dir_open(struct inode *inode, struct file *file) return PTR_ERR(realfile); } od->realfile = realfile; - od->is_real = !OVL_TYPE_MERGE(type); + od->is_real = ovl_dir_is_real(file->f_path.dentry); od->is_upper = OVL_TYPE_UPPER(type); file->private_data = od; |