fs: add file_dentry()
Subsystems (3)
Miklos Szeredi
maintainerPATCH v2 1/1 fs: add file_dentry()
This series fixes bugs in nfs and ext4 due to 4bacc9c9234c ("overlayfs:
Make f_path always point to the overlay and f_inode to the underlay").
Regular files opened on overlayfs will result in the file being opened on the underlying filesystem, while f_path points to the overlayfs mount/dentry.
This confuses filesystems which get the dentry from struct file and assume it's theirs.
Add a new helper, file_dentry() [*], to get the filesystem's own dentry from the file. This checks file->f_path.dentry->d_flags against DCACHE_OP_REAL, and returns file->f_path.dentry if DCACHE_OP_REAL is not set (this is the common, non-overlayfs case).
In the uncommon case it will call into overlayfs's ->d_real() to get the underlying dentry, matching file_inode(file).
The reason we need to check against the inode is that if the file is copied up while being open, d_real() would return the upper dentry, while the open file comes from the lower dentry.
[*] If possible, it's better simply to use file_inode() instead.
---
changes in v2:
- rename method to d_op->d_real()
- check d_flag instead of comparing inode for base dentry
- ovl_d_real() now handles recursion
- WARN instead of BUG and be more verbose
- add d_real() helper too
fs/dcache.c | 5 ++++- fs/overlayfs/super.c | 33 +++++++++++++++++++++++++++++++++ include/linux/dcache.h | 10 ++++++++++ include/linux/fs.h | 10 ++++++++++ 4 files changed, 57 insertions(+), 1 deletion(-)
fs/dcache.c +4 -1
@@ -1667,7 +1667,8 @@ void d_set_d_op(struct dentry *dentry, cDCACHE_OP_REVALIDATE |DCACHE_OP_WEAK_REVALIDATE |DCACHE_OP_DELETE | - DCACHE_OP_SELECT_INODE)); +DCACHE_OP_SELECT_INODE | + DCACHE_OP_REAL)); dentry->d_op = op;if (!op)return ;
@@ -1685,6 +1686,8 @@ void d_set_d_op(struct dentry *dentry, c dentry->d_flags |=DCACHE_OP_PRUNE ;if (op->d_select_inode) dentry->d_flags |=DCACHE_OP_SELECT_INODE ; +if (op->d_real) + dentry->d_flags |=DCACHE_OP_REAL ; }EXPORT_SYMBOL (d_set_d_op);
include/linux/dcache.h +10 -0
@@ -161,6 +161,7 @@ struct dentry_operations {struct vfsmount *(*d_automount)(struct path *);int (*d_manage)(struct dentry *,bool );struct inode *(*d_select_inode)(struct dentry *,unsigned ); +struct dentry *(*d_real)(struct dentry *,struct inode *); }____cacheline_aligned ;/*
@@ -229,6 +230,7 @@ struct dentry_operations {#define DCACHE_OP_SELECT_INODE 0x02000000/* Unioned entry: dcache op selects inode */ #define DCACHE_ENCRYPTED_WITH_KEY 0x04000000/* dir is encrypted with a valid key */ +#define DCACHE_OP_REAL 0x08000000extern seqlock_t rename_lock;
@@ -555,4 +557,12 @@ static inline struct dentry *d_backing_d return upper; } +static inline struct dentry *d_real (struct dentry *dentry) +{ +if (unlikely (dentry->d_flags &DCACHE_OP_REAL )) +return dentry->d_op->d_real (dentry,NULL ); +else +return dentry; +} +#endif /* __LINUX_DCACHE_H */
fs/overlayfs/super.c +33 -0
@@ -295,6 +295,37 @@ static void ovl_dentry_release(struct de } } +static struct dentry *ovl_d_real (struct dentry *dentry,struct inode *inode) +{ +struct dentry *real; + +if (d_is_dir (dentry)) { +if (!inode || inode ==d_inode (dentry)) +return dentry; + goto bug; + } + + real =ovl_dentry_upper (dentry); +if (real && (!inode || inode ==d_inode (real))) +return real; + + real =ovl_dentry_lower (dentry); +if (!real) + goto bug; + +if (!inode || inode ==d_inode (real)) +return real; + +/* Handle recursion */ +if (real->d_flags &DCACHE_OP_REAL ) +return real->d_op->d_real (real, inode); + +bug: +WARN (1 ,"ovl_d_real(%pd4, %s:%lu\n): real dentry not found\n" , dentry, + inode ? inode->i_sb->s_id :"NULL" , inode ? inode->i_ino :0 ); +return dentry; +} +static int ovl_dentry_revalidate (struct dentry *dentry,unsigned int flags) {struct ovl_entry *oe = dentry->d_fsdata;
@@ -339,11 +370,13 @@ static int ovl_dentry_weak_revalidate(ststatic const struct dentry_operations ovl_dentry_operations = { .d_release = ovl_dentry_release, .d_select_inode = ovl_d_select_inode, + .d_real = ovl_d_real, };static const struct dentry_operations ovl_reval_dentry_operations = { .d_release = ovl_dentry_release, .d_select_inode = ovl_d_select_inode, + .d_real = ovl_d_real, .d_revalidate = ovl_dentry_revalidate, .d_weak_revalidate = ovl_dentry_weak_revalidate, };
include/linux/fs.h +10 -0
@@ -1241,6 +1241,16 @@ static inline struct inode *file_inode(creturn f->f_inode; } +static inline struct dentry *file_dentry (const struct file *file) +{ +struct dentry *dentry = file->f_path.dentry; + +if (unlikely (dentry->d_flags &DCACHE_OP_REAL )) +return dentry->d_op->d_real (dentry,file_inode (file)); +else +return dentry; +} +static inline int locks_lock_file_wait (struct file *filp,struct file_lock *fl) {return locks_lock_inode_wait (file_inode (filp), fl);
Hello Miklos,
Quoted 22 lines · Miklos Szeredi
This series fixes bugs in nfs and ext4 due to
4bacc9c9234c("overlayfs:
Makef_pathalways point to the overlay andf_inodeto the underlay").Regular files opened on overlayfs will result in the file being opened on the underlying filesystem, while
f_pathpoints to the overlayfs mount/dentry.This confuses filesystems which get the dentry from struct file and assume it's theirs.
Add a new helper,
file_dentry()[*], to get the filesystem's own dentry from the file. This checksfile->f_path.dentry->d_flagsagainstDCACHE_OP_REAL, and returnsfile->f_path.dentryifDCACHE_OP_REALis not set (this is the common, non-overlayfs case).In the uncommon case it will call into overlayfs's ->d_real() to get the underlying dentry, matching
file_inode(file).The reason we need to check against the inode is that if the file is copied up while being open,
d_real()would return the upper dentry, while the open file comes from the lower dentry.
Quoted 6 lines
[*] If possible, it's better simply to use
file_inode()instead.Signed-off-by Miklos Szeredi <[email protected]>Tested-by Goldwyn Rodrigues <[email protected]>Reviewed-by Trond Myklebust <[email protected]>Cc <[email protected]> # v4.2
4bacc9c9234c ("overlayfs: Make f_path always point to the overlay and f_inode to the underlay").
This commit is from v4.1 if I am not wrong. So the stable tag might be wrong.
Am I missing something?
Signature · 1 line
William
Miklos Szeredi
maintainerQuoted 5 lines · William Dauchy
4bacc9c9234c("overlayfs: Makef_pathalways point to the overlay andf_inodeto the underlay").
This commit is from v4.1 if I am not wrong. So the stable tag might be wrong.Am I missing something?
Use "--contains", otherwise "git describe" will just say which kernel the patch has been committed to, not which release it appears in.
git describe --contains 4bacc9c9234c
v4.2-rc1~2^2~27
Thanks,
Miklos
Quoted 5 lines · Miklos Szeredi
Use "--contains", otherwise "git describe" will just say which kernel the patch has been committed to, not which release it appears in.
git describe --contains
4bacc9c9234c
v4.2-rc1~2^2~27
Indeed; I saw the patch in v4.1.x stable
https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/commit/fs/internal.h?h=linux-4.1.y&id=9abb3b81094857a1e2d7dea5b2a8605e29d8c77d
Signature · 1 line
William
Hey Miklos,
Quoted 2 lines · Miklos Szeredi
This series fixes bugs in nfs and ext4 due to
4bacc9c9234c("overlayfs:
Makef_pathalways point to the overlay andf_inodeto the underlay").
Since that commit got backported into older -stable kernel, it would appear that this file_dentry() series is relevant for pre-4.2 kernels as well.
However, backporting it seems to be less than trivial.
Could you provide a backport for older -stable kernels please?
Thanks,
Sasha
Quoted 6 lines · Sasha Levin
Hey Miklos,
Quoted 2 lines · Miklos Szeredi
This series fixes bugs in nfs and ext4 due to
4bacc9c9234c("overlayfs:
Makef_pathalways point to the overlay andf_inodeto the underlay").
Quoted 7 lines
Since that commit got backported into older -stable kernel, it would appear that this
file_dentry()series is relevant for pre-4.2 kernels as well.However, backporting it seems to be less than trivial.
Could you provide a backport for older -stable kernels please?
From: Miklos Szeredi <[email protected]>
Date: Sat, 26 Mar 2016 16:14:37 -0400
Subject: fs: add file_dentry()
commit d101a125954eae1d397adda94ca6319485a50493 upstream.
This series fixes bugs in nfs and ext4 due to 4bacc9c9234c ("overlayfs:
Make f_path always point to the overlay and f_inode to the underlay").
Regular files opened on overlayfs will result in the file being opened on the underlying filesystem, while f_path points to the overlayfs mount/dentry.
This confuses filesystems which get the dentry from struct file and assume it's theirs.
Add a new helper, file_dentry() [*], to get the filesystem's own dentry from the file. This checks file->f_path.dentry->d_flags against DCACHE_OP_REAL, and returns file->f_path.dentry if DCACHE_OP_REAL is not set (this is the common, non-overlayfs case).
In the uncommon case it will call into overlayfs's ->d_real() to get the underlying dentry, matching file_inode(file).
The reason we need to check against the inode is that if the file is copied up while being open, d_real() would return the upper dentry, while the open file comes from the lower dentry.
[*] If possible, it's better simply to use file_inode() instead.
--- fs/dcache.c | 5 ++++- fs/overlayfs/super.c | 32 ++++++++++++++++++++++++++++++++ include/linux/dcache.h | 10 ++++++++++ include/linux/fs.h | 10 ++++++++++ 4 files changed, 56 insertions(+), 1 deletion(-)
fs/dcache.c +4 -1
@@ -1666,7 +1666,8 @@ void d_set_d_op(struct dentry *dentry, cDCACHE_OP_REVALIDATE |DCACHE_OP_WEAK_REVALIDATE |DCACHE_OP_DELETE | - DCACHE_OP_SELECT_INODE)); +DCACHE_OP_SELECT_INODE | + DCACHE_OP_REAL)); dentry->d_op = op;if (!op)return ;
@@ -1684,6 +1685,8 @@ void d_set_d_op(struct dentry *dentry, c dentry->d_flags |=DCACHE_OP_PRUNE ;if (op->d_select_inode) dentry->d_flags |=DCACHE_OP_SELECT_INODE ; +if (op->d_real) + dentry->d_flags |=DCACHE_OP_REAL ; }EXPORT_SYMBOL (d_set_d_op);
fs/overlayfs/super.c +32 -0
@@ -276,9 +276,41 @@ static void ovl_dentry_release(struct de } } +static struct dentry *ovl_d_real (struct dentry *dentry,struct inode *inode) +{ +struct dentry *real; + +if (d_is_dir (dentry)) { +if (!inode || inode ==d_inode (dentry)) +return dentry; + goto bug; + } + + real =ovl_dentry_upper (dentry); +if (real && (!inode || inode ==d_inode (real))) +return real; + + real =ovl_dentry_lower (dentry); +if (!real) + goto bug; + +if (!inode || inode ==d_inode (real)) +return real; + +/* Handle recursion */ +if (real->d_flags &DCACHE_OP_REAL ) +return real->d_op->d_real (real, inode); + +bug: +WARN (1 ,"ovl_d_real(%pd4, %s:%lu\n): real dentry not found\n" , dentry, + inode ? inode->i_sb->s_id :"NULL" , inode ? inode->i_ino :0 ); +return dentry; +} +static const struct dentry_operations ovl_dentry_operations = { .d_release = ovl_dentry_release, .d_select_inode = ovl_d_select_inode, + .d_real = ovl_d_real, };static struct ovl_entry *ovl_alloc_entry (unsigned int numlower)
include/linux/dcache.h +10 -0
@@ -161,6 +161,7 @@ struct dentry_operations {struct vfsmount *(*d_automount)(struct path *);int (*d_manage)(struct dentry *,bool );struct inode *(*d_select_inode)(struct dentry *,unsigned ); +struct dentry *(*d_real)(struct dentry *,struct inode *); }____cacheline_aligned ;/*
@@ -227,6 +228,7 @@ struct dentry_operations {#define DCACHE_MAY_FREE 0x00800000#define DCACHE_FALLTHRU 0x01000000/* Fall through to lower layer */ #define DCACHE_OP_SELECT_INODE 0x02000000/* Unioned entry: dcache op selects inode */ +#define DCACHE_OP_REAL 0x08000000extern seqlock_t rename_lock;
@@ -576,4 +578,12 @@ static inline struct dentry *d_backing_d return upper; } +static inline struct dentry *d_real (struct dentry *dentry) +{ +if (unlikely (dentry->d_flags &DCACHE_OP_REAL )) +return dentry->d_op->d_real (dentry,NULL ); +else +return dentry; +} +#endif /* __LINUX_DCACHE_H */
include/linux/fs.h +10 -0
@@ -1199,6 +1199,16 @@ static inline struct inode *file_inode(creturn f->f_inode; } +static inline struct dentry *file_dentry (const struct file *file) +{ +struct dentry *dentry = file->f_path.dentry; + +if (unlikely (dentry->d_flags &DCACHE_OP_REAL )) +return dentry->d_op->d_real (dentry,file_inode (file)); +else +return dentry; +} +static inline int posix_lock_file_wait (struct file *filp,struct file_lock *fl) {return posix_lock_inode_wait (file_inode (filp), fl);