| From 9a1798e2abb1cb9d286a926465015a16a8a320d4 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Sun, 11 Apr 2021 12:22:23 +0300 |
| Subject: ovl: invalidate readdir cache on changes to dir with origin |
| |
| From: Amir Goldstein <amir73il@gmail.com> |
| |
| [ Upstream commit 65cd913ec9d9d71529665924c81015b7ab7d9381 ] |
| |
| The test in ovl_dentry_version_inc() was out-dated and did not include |
| the case where readdir cache is used on a non-merge dir that has origin |
| xattr, indicating that it may contain leftover whiteouts. |
| |
| To make the code more robust, use the same helper ovl_dir_is_real() |
| to determine if readdir cache should be used and if readdir cache should |
| be invalidated. |
| |
| Fixes: b79e05aaa166 ("ovl: no direct iteration for dir with origin xattr") |
| Link: https://lore.kernel.org/linux-unionfs/CAOQ4uxht70nODhNHNwGFMSqDyOKLXOKrY0H6g849os4BQ7cokA@mail.gmail.com/ |
| Cc: Chris Murphy <lists@colorremedies.com> |
| Signed-off-by: Amir Goldstein <amir73il@gmail.com> |
| Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| fs/overlayfs/overlayfs.h | 30 +++++++++++++++++++++++++++--- |
| fs/overlayfs/readdir.c | 12 ------------ |
| fs/overlayfs/util.c | 31 +++++++++---------------------- |
| 3 files changed, 36 insertions(+), 37 deletions(-) |
| |
| diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h |
| index 9f7af98ae200..e43dc68bd1b5 100644 |
| --- a/fs/overlayfs/overlayfs.h |
| +++ b/fs/overlayfs/overlayfs.h |
| @@ -308,9 +308,6 @@ int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry, |
| enum ovl_xattr ox, const void *value, size_t size, |
| int xerr); |
| int ovl_set_impure(struct dentry *dentry, struct dentry *upperdentry); |
| -void ovl_set_flag(unsigned long flag, struct inode *inode); |
| -void ovl_clear_flag(unsigned long flag, struct inode *inode); |
| -bool ovl_test_flag(unsigned long flag, struct inode *inode); |
| bool ovl_inuse_trylock(struct dentry *dentry); |
| void ovl_inuse_unlock(struct dentry *dentry); |
| bool ovl_is_inuse(struct dentry *dentry); |
| @@ -324,6 +321,21 @@ char *ovl_get_redirect_xattr(struct ovl_fs *ofs, struct dentry *dentry, |
| int padding); |
| int ovl_sync_status(struct ovl_fs *ofs); |
| |
| +static inline void ovl_set_flag(unsigned long flag, struct inode *inode) |
| +{ |
| + set_bit(flag, &OVL_I(inode)->flags); |
| +} |
| + |
| +static inline void ovl_clear_flag(unsigned long flag, struct inode *inode) |
| +{ |
| + clear_bit(flag, &OVL_I(inode)->flags); |
| +} |
| + |
| +static inline bool ovl_test_flag(unsigned long flag, struct inode *inode) |
| +{ |
| + return test_bit(flag, &OVL_I(inode)->flags); |
| +} |
| + |
| static inline bool ovl_is_impuredir(struct super_block *sb, |
| struct dentry *dentry) |
| { |
| @@ -427,6 +439,18 @@ int ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt, |
| struct dentry *dentry, int level); |
| int ovl_indexdir_cleanup(struct ovl_fs *ofs); |
| |
| +/* |
| + * 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 inline bool ovl_dir_is_real(struct dentry *dir) |
| +{ |
| + return !ovl_test_flag(OVL_WHITEOUTS, d_inode(dir)); |
| +} |
| + |
| /* inode.c */ |
| int ovl_set_nlink_upper(struct dentry *dentry); |
| int ovl_set_nlink_lower(struct dentry *dentry); |
| diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c |
| index f404a78e6b60..cc1e80257064 100644 |
| --- a/fs/overlayfs/readdir.c |
| +++ b/fs/overlayfs/readdir.c |
| @@ -319,18 +319,6 @@ 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; |
| diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c |
| index 6e7b8c882045..e8b14d2c180c 100644 |
| --- a/fs/overlayfs/util.c |
| +++ b/fs/overlayfs/util.c |
| @@ -419,18 +419,20 @@ void ovl_inode_update(struct inode *inode, struct dentry *upperdentry) |
| } |
| } |
| |
| -static void ovl_dentry_version_inc(struct dentry *dentry, bool impurity) |
| +static void ovl_dir_version_inc(struct dentry *dentry, bool impurity) |
| { |
| struct inode *inode = d_inode(dentry); |
| |
| WARN_ON(!inode_is_locked(inode)); |
| + WARN_ON(!d_is_dir(dentry)); |
| /* |
| - * Version is used by readdir code to keep cache consistent. For merge |
| - * dirs all changes need to be noted. For non-merge dirs, cache only |
| - * contains impure (ones which have been copied up and have origins) |
| - * entries, so only need to note changes to impure entries. |
| + * Version is used by readdir code to keep cache consistent. |
| + * For merge dirs (or dirs with origin) all changes need to be noted. |
| + * For non-merge dirs, cache contains only impure entries (i.e. ones |
| + * which have been copied up and have origins), so only need to note |
| + * changes to impure entries. |
| */ |
| - if (OVL_TYPE_MERGE(ovl_path_type(dentry)) || impurity) |
| + if (!ovl_dir_is_real(dentry) || impurity) |
| OVL_I(inode)->version++; |
| } |
| |
| @@ -439,7 +441,7 @@ void ovl_dir_modified(struct dentry *dentry, bool impurity) |
| /* Copy mtime/ctime */ |
| ovl_copyattr(d_inode(ovl_dentry_upper(dentry)), d_inode(dentry)); |
| |
| - ovl_dentry_version_inc(dentry, impurity); |
| + ovl_dir_version_inc(dentry, impurity); |
| } |
| |
| u64 ovl_dentry_version_get(struct dentry *dentry) |
| @@ -634,21 +636,6 @@ int ovl_set_impure(struct dentry *dentry, struct dentry *upperdentry) |
| return err; |
| } |
| |
| -void ovl_set_flag(unsigned long flag, struct inode *inode) |
| -{ |
| - set_bit(flag, &OVL_I(inode)->flags); |
| -} |
| - |
| -void ovl_clear_flag(unsigned long flag, struct inode *inode) |
| -{ |
| - clear_bit(flag, &OVL_I(inode)->flags); |
| -} |
| - |
| -bool ovl_test_flag(unsigned long flag, struct inode *inode) |
| -{ |
| - return test_bit(flag, &OVL_I(inode)->flags); |
| -} |
| - |
| /** |
| * Caller must hold a reference to inode to prevent it from being freed while |
| * it is marked inuse. |
| -- |
| 2.30.2 |
| |