The two methods essentially do the same: find the real dentry/inode belonging to an overlay dentry. The difference is in the usage: vfs_open() uses ->d_select_inode() and expects the function to perform copy-up if necessary based on the open flags argument. file_dentry() uses ->d_real() passing in the overlay dentry as well as the underlying inode. vfs_rename() uses ->d_select_inode() but passes zero flags. ->d_real() with a zero inode would have worked just as well here. This patch merges the functionality of ->d_select_inode() into ->d_real() by adding an 'open_flags' argument to the latter. Signed-off-by: Miklos Szeredi <mszeredi@xxxxxxxxxx> --- fs/dcache.c | 3 --- fs/namei.c | 2 +- fs/open.c | 17 +++++++++++++---- fs/overlayfs/inode.c | 31 ++++++++++--------------------- fs/overlayfs/overlayfs.h | 2 +- fs/overlayfs/super.c | 17 +++++++++++++---- include/linux/dcache.h | 22 ++++------------------ include/linux/fs.h | 2 +- 8 files changed, 43 insertions(+), 53 deletions(-) --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1667,7 +1667,6 @@ void d_set_d_op(struct dentry *dentry, c DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE | DCACHE_OP_DELETE | - DCACHE_OP_SELECT_INODE | DCACHE_OP_REAL)); dentry->d_op = op; if (!op) @@ -1684,8 +1683,6 @@ void d_set_d_op(struct dentry *dentry, c dentry->d_flags |= DCACHE_OP_DELETE; if (op->d_prune) 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; --- a/fs/namei.c +++ b/fs/namei.c @@ -4247,7 +4247,7 @@ int vfs_rename(struct inode *old_dir, st * Check source == target. * On overlayfs need to look at underlying inodes. */ - if (vfs_select_inode(old_dentry, 0) == vfs_select_inode(new_dentry, 0)) + if (d_inode(d_real(old_dentry)) == d_inode(d_real(new_dentry))) return 0; error = may_delete(old_dir, old_dentry, is_dir); --- a/fs/open.c +++ b/fs/open.c @@ -831,6 +831,15 @@ char *file_path(struct file *filp, char } EXPORT_SYMBOL(file_path); +static struct dentry *open_select_dentry(struct dentry *dentry, + unsigned int open_flags) +{ + if (unlikely(dentry->d_flags & DCACHE_OP_REAL)) + return dentry->d_op->d_real(dentry, NULL, open_flags); + else + return dentry; +} + /** * vfs_open - open the file at the given path * @path: path to open @@ -840,13 +849,13 @@ EXPORT_SYMBOL(file_path); int vfs_open(const struct path *path, struct file *file, const struct cred *cred) { - struct inode *inode = vfs_select_inode(path->dentry, file->f_flags); + struct dentry *dentry = open_select_dentry(path->dentry, file->f_flags); - if (IS_ERR(inode)) - return PTR_ERR(inode); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); file->f_path = *path; - return do_dentry_open(file, inode, NULL, cred); + return do_dentry_open(file, d_backing_inode(dentry), NULL, cred); } struct file *dentry_open(const struct path *path, int flags, --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -338,36 +338,25 @@ static bool ovl_open_need_copy_up(int fl return true; } -struct inode *ovl_d_select_inode(struct dentry *dentry, unsigned file_flags) +int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags) { - int err; + int err = 0; struct path realpath; enum ovl_path_type type; - if (d_is_dir(dentry)) - return d_backing_inode(dentry); - type = ovl_path_real(dentry, &realpath); if (ovl_open_need_copy_up(file_flags, type, realpath.dentry)) { err = ovl_want_write(dentry); - if (err) - return ERR_PTR(err); - - if (file_flags & O_TRUNC) - err = ovl_copy_up_truncate(dentry); - else - err = ovl_copy_up(dentry); - ovl_drop_write(dentry); - if (err) - return ERR_PTR(err); - - ovl_path_upper(dentry, &realpath); + if (!err) { + if (file_flags & O_TRUNC) + err = ovl_copy_up_truncate(dentry); + else + err = ovl_copy_up(dentry); + ovl_drop_write(dentry); + } } - if (realpath.dentry->d_flags & DCACHE_OP_SELECT_INODE) - return realpath.dentry->d_op->d_select_inode(realpath.dentry, file_flags); - - return d_backing_inode(realpath.dentry); + return err; } static const struct inode_operations ovl_file_inode_operations = { --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -177,7 +177,7 @@ ssize_t ovl_getxattr(struct dentry *dent void *value, size_t size); ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size); int ovl_removexattr(struct dentry *dentry, const char *name); -struct inode *ovl_d_select_inode(struct dentry *dentry, unsigned file_flags); +int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags); struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, struct ovl_entry *oe); --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -295,7 +295,8 @@ static void ovl_dentry_release(struct de } } -static struct dentry *ovl_d_real(struct dentry *dentry, struct inode *inode) +static struct dentry *ovl_d_real(struct dentry *dentry, struct inode *inode, + unsigned int open_flags) { struct dentry *real; @@ -305,6 +306,16 @@ static struct dentry *ovl_d_real(struct goto bug; } + if (d_is_negative(dentry)) + return dentry; + + if (open_flags) { + int err = ovl_open_maybe_copy_up(dentry, open_flags); + + if (err) + return ERR_PTR(err); + } + real = ovl_dentry_upper(dentry); if (real && (!inode || inode == d_inode(real))) return real; @@ -318,7 +329,7 @@ static struct dentry *ovl_d_real(struct /* Handle recursion */ if (real->d_flags & DCACHE_OP_REAL) - return real->d_op->d_real(real, inode); + return real->d_op->d_real(real, inode, open_flags); bug: WARN(1, "ovl_d_real(%pd4, %s:%lu\n): real dentry not found\n", dentry, @@ -369,13 +380,11 @@ static int ovl_dentry_weak_revalidate(st 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 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, --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -160,8 +160,7 @@ struct dentry_operations { char *(*d_dname)(struct dentry *, char *, int); 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 *); + struct dentry *(*d_real)(struct dentry *, struct inode *, unsigned int); } ____cacheline_aligned; /* @@ -227,10 +226,8 @@ 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_ENCRYPTED_WITH_KEY 0x04000000 /* dir is encrypted with a valid key */ -#define DCACHE_OP_REAL 0x08000000 +#define DCACHE_ENCRYPTED_WITH_KEY 0x02000000 /* dir is encrypted with a valid key */ +#define DCACHE_OP_REAL 0x04000000 extern seqlock_t rename_lock; @@ -560,21 +557,10 @@ static inline struct dentry *d_backing_d 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); + return dentry->d_op->d_real(dentry, NULL, 0); else return dentry; } -static inline struct inode *vfs_select_inode(struct dentry *dentry, - unsigned open_flags) -{ - struct inode *inode = d_inode(dentry); - - if (inode && unlikely(dentry->d_flags & DCACHE_OP_SELECT_INODE)) - inode = dentry->d_op->d_select_inode(dentry, open_flags); - - return inode; -} - #endif /* __LINUX_DCACHE_H */ --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1246,7 +1246,7 @@ static inline struct dentry *file_dentry 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)); + return dentry->d_op->d_real(dentry, file_inode(file), 0); else return dentry; } -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html