Overlayfs knows the real path of underlying dentries. Add an optional struct vfsmount out argument to ->d_real(), so callers could compose the real path. Add a helper f_real_path() that uses this new interface to return the real path of f_inode, for overlayfs internal files whose f_path if a "fake" overlayfs path and f_inode is the underlying real inode. Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx> --- Documentation/filesystems/locking.rst | 3 ++- Documentation/filesystems/vfs.rst | 3 ++- fs/file_table.c | 23 +++++++++++++++++++++++ fs/overlayfs/super.c | 27 ++++++++++++++++++--------- include/linux/dcache.h | 11 +++++++---- include/linux/fs.h | 4 +++- 6 files changed, 55 insertions(+), 16 deletions(-) diff --git a/Documentation/filesystems/locking.rst b/Documentation/filesystems/locking.rst index aa1a233b0fa8..a6063b0c79fd 100644 --- a/Documentation/filesystems/locking.rst +++ b/Documentation/filesystems/locking.rst @@ -29,7 +29,8 @@ prototypes:: char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen); struct vfsmount *(*d_automount)(struct path *path); int (*d_manage)(const struct path *, bool); - struct dentry *(*d_real)(struct dentry *, const struct inode *); + struct dentry *(*d_real)(struct dentry *, const struct inode *, + struct vfsmount **pmnt); locking rules: diff --git a/Documentation/filesystems/vfs.rst b/Documentation/filesystems/vfs.rst index 769be5230210..edafe824fca4 100644 --- a/Documentation/filesystems/vfs.rst +++ b/Documentation/filesystems/vfs.rst @@ -1264,7 +1264,8 @@ defined: char *(*d_dname)(struct dentry *, char *, int); struct vfsmount *(*d_automount)(struct path *); int (*d_manage)(const struct path *, bool); - struct dentry *(*d_real)(struct dentry *, const struct inode *); + struct dentry *(*d_real)(struct dentry *, const struct inode *, + struct vfsmount **pmnt); }; ``d_revalidate`` diff --git a/fs/file_table.c b/fs/file_table.c index d64d3933f3e4..fa187ceb54d6 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -215,6 +215,29 @@ struct file *alloc_empty_file_internal(int flags, const struct cred *cred) return f; } +/** + * f_real_path - return the real path of an internal file with fake path + * + * @file: The file to query + * + * If f_path is on a union/overlay and f_inode is not, then return the + * underlying real path of f_inode. + * Otherwise return f_path (by value). + */ +struct path f_real_path(const struct file *f) +{ + struct path path; + + if (!(f->f_mode & FMODE_INTERNAL) || + (d_inode(f->f_path.dentry) == f->f_inode)) + return f->f_path; + + path.mnt = f->f_path.mnt; + path.dentry = d_real(f->f_path.dentry, f->f_inode, &path.mnt); + return path; +} +EXPORT_SYMBOL(f_real_path); + /** * alloc_file - allocate and initialize a 'struct file' * diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index d9be5d318e1b..591c77b33ff3 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -60,9 +60,11 @@ MODULE_PARM_DESC(metacopy, "Default to on or off for the metadata only copy up feature"); static struct dentry *ovl_d_real(struct dentry *dentry, - const struct inode *inode) + const struct inode *inode, + struct vfsmount **pmnt) { struct dentry *real = NULL, *lower; + struct path realpath; /* It's an overlay file */ if (inode && d_inode(dentry) == inode) @@ -74,12 +76,13 @@ static struct dentry *ovl_d_real(struct dentry *dentry, goto bug; } - real = ovl_dentry_upper(dentry); + ovl_path_upper(dentry, &realpath); + real = realpath.dentry; if (real && (inode == d_inode(real))) - return real; + goto found; if (real && !inode && ovl_has_upperdata(d_inode(dentry))) - return real; + goto found; /* * Best effort lazy lookup of lowerdata for !inode case to return @@ -90,16 +93,22 @@ static struct dentry *ovl_d_real(struct dentry *dentry, * when setting the uprobe. */ ovl_maybe_lookup_lowerdata(dentry); - lower = ovl_dentry_lowerdata(dentry); + ovl_path_lowerdata(dentry, &realpath); + lower = realpath.dentry; if (!lower) goto bug; - real = lower; /* Handle recursion */ - real = d_real(real, inode); + real = d_real(lower, inode, &realpath.mnt); + + if (inode && inode != d_inode(real)) + goto bug; + +found: + if (pmnt) + *pmnt = realpath.mnt; + return real; - if (!inode || inode == d_inode(real)) - return real; bug: WARN(1, "%s(%pd4, %s:%lu): real dentry (%p/%lu) not found\n", __func__, dentry, inode ? inode->i_sb->s_id : "NULL", diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 6b351e009f59..78a54d175662 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -139,7 +139,8 @@ struct dentry_operations { char *(*d_dname)(struct dentry *, char *, int); struct vfsmount *(*d_automount)(struct path *); int (*d_manage)(const struct path *, bool); - struct dentry *(*d_real)(struct dentry *, const struct inode *); + struct dentry *(*d_real)(struct dentry *, const struct inode *, + struct vfsmount **); } ____cacheline_aligned; /* @@ -564,6 +565,7 @@ static inline struct dentry *d_backing_dentry(struct dentry *upper) * d_real - Return the real dentry * @dentry: the dentry to query * @inode: inode to select the dentry from multiple layers (can be NULL) + * @pmnt: returns the real mnt in case @dentry is not real * * If dentry is on a union/overlay, then return the underlying, real dentry. * Otherwise return the dentry itself. @@ -571,10 +573,11 @@ static inline struct dentry *d_backing_dentry(struct dentry *upper) * See also: Documentation/filesystems/vfs.rst */ static inline struct dentry *d_real(struct dentry *dentry, - const struct inode *inode) + const struct inode *inode, + struct vfsmount **pmnt) { if (unlikely(dentry->d_flags & DCACHE_OP_REAL)) - return dentry->d_op->d_real(dentry, inode); + return dentry->d_op->d_real(dentry, inode, pmnt); else return dentry; } @@ -589,7 +592,7 @@ static inline struct dentry *d_real(struct dentry *dentry, static inline struct inode *d_real_inode(const struct dentry *dentry) { /* This usage of d_real() results in const dentry */ - return d_backing_inode(d_real((struct dentry *) dentry, NULL)); + return d_inode(d_real((struct dentry *) dentry, NULL, NULL)); } struct name_snapshot { diff --git a/include/linux/fs.h b/include/linux/fs.h index 13eec1e8ca86..d0129e9e0ae5 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1042,7 +1042,7 @@ static inline struct inode *file_inode(const struct file *f) static inline struct dentry *file_dentry(const struct file *file) { - return d_real(file->f_path.dentry, file_inode(file)); + return d_real(file->f_path.dentry, file_inode(file), NULL); } struct fasync_struct { @@ -2354,6 +2354,8 @@ extern struct file *dentry_create(const struct path *path, int flags, umode_t mode, const struct cred *cred); extern struct file * open_with_fake_path(const struct path *, int, struct inode*, const struct cred *); +extern struct path f_real_path(const struct file *f); + static inline struct file *file_clone_open(struct file *file) { return dentry_open(&file->f_path, file->f_flags, file->f_cred); -- 2.34.1