From d614146d18159e54d4dfc289e4c1534ccc485cf2 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 28 Jul 2016 13:05:50 -0400 Subject: fold dentry_rcuwalk_invalidate() into its only remaining caller Signed-off-by: Al Viro --- fs/dcache.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index b90cf8e09d5b..6a629f50c3cf 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -316,20 +316,6 @@ static void dentry_free(struct dentry *dentry) call_rcu(&dentry->d_u.d_rcu, __d_free); } -/** - * dentry_rcuwalk_invalidate - invalidate in-progress rcu-walk lookups - * @dentry: the target dentry - * After this call, in-progress rcu-walk path lookup will fail. This - * should be called after unhashing, and after changing d_inode (if - * the dentry has not already been unhashed). - */ -static inline void dentry_rcuwalk_invalidate(struct dentry *dentry) -{ - lockdep_assert_held(&dentry->d_lock); - /* Go through am invalidation barrier */ - write_seqcount_invalidate(&dentry->d_seq); -} - /* * Release the dentry's inode, using the filesystem * d_iput() operation if defined. @@ -468,7 +454,8 @@ void __d_drop(struct dentry *dentry) __hlist_bl_del(&dentry->d_hash); dentry->d_hash.pprev = NULL; hlist_bl_unlock(b); - dentry_rcuwalk_invalidate(dentry); + /* After this call, in-progress rcu-walk path lookup will fail. */ + write_seqcount_invalidate(&dentry->d_seq); } } EXPORT_SYMBOL(__d_drop); -- cgit v1.2.3 From 15d3c589f6305c50a705572dbb33620c5cba416c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 29 Jul 2016 17:45:21 -0400 Subject: fold _d_rehash() and __d_rehash() together The only place where we feed to __d_rehash() something other than d_hash(dentry->d_name.hash) is __d_move(), where we give it d_hash of another dentry. Postpone rehashing until we'd switched the names and we are rid of that exception, along with the need to keep _d_rehash() and __d_rehash() separate. Signed-off-by: Al Viro --- fs/dcache.c | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index 6a629f50c3cf..d9450bd496ac 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2339,19 +2339,15 @@ again: } EXPORT_SYMBOL(d_delete); -static void __d_rehash(struct dentry * entry, struct hlist_bl_head *b) +static void __d_rehash(struct dentry *entry) { + struct hlist_bl_head *b = d_hash(entry->d_name.hash); BUG_ON(!d_unhashed(entry)); hlist_bl_lock(b); hlist_bl_add_head_rcu(&entry->d_hash, b); hlist_bl_unlock(b); } -static void _d_rehash(struct dentry * entry) -{ - __d_rehash(entry, d_hash(entry->d_name.hash)); -} - /** * d_rehash - add an entry back to the hash * @entry: dentry to add to the hash @@ -2362,7 +2358,7 @@ static void _d_rehash(struct dentry * entry) void d_rehash(struct dentry * entry) { spin_lock(&entry->d_lock); - _d_rehash(entry); + __d_rehash(entry); spin_unlock(&entry->d_lock); } EXPORT_SYMBOL(d_rehash); @@ -2536,7 +2532,7 @@ static inline void __d_add(struct dentry *dentry, struct inode *inode) raw_write_seqcount_end(&dentry->d_seq); fsnotify_update_flags(dentry); } - _d_rehash(dentry); + __d_rehash(dentry); if (dir) end_dir_add(dir, n); spin_unlock(&dentry->d_lock); @@ -2598,7 +2594,7 @@ struct dentry *d_exact_alias(struct dentry *entry, struct inode *inode) alias = NULL; } else { __dget_dlock(alias); - _d_rehash(alias); + __d_rehash(alias); spin_unlock(&alias->d_lock); } spin_unlock(&inode->i_lock); @@ -2782,23 +2778,10 @@ static void __d_move(struct dentry *dentry, struct dentry *target, write_seqcount_begin(&dentry->d_seq); write_seqcount_begin_nested(&target->d_seq, DENTRY_D_LOCK_NESTED); + /* unhash both */ /* __d_drop does write_seqcount_barrier, but they're OK to nest. */ - - /* - * Move the dentry to the target hash queue. Don't bother checking - * for the same hash queue because of how unlikely it is. - */ __d_drop(dentry); - __d_rehash(dentry, d_hash(target->d_name.hash)); - - /* - * Unhash the target (d_delete() is not usable here). If exchanging - * the two dentries, then rehash onto the other's hash queue. - */ __d_drop(target); - if (exchange) { - __d_rehash(target, d_hash(dentry->d_name.hash)); - } /* Switch the names.. */ if (exchange) @@ -2806,6 +2789,11 @@ static void __d_move(struct dentry *dentry, struct dentry *target, else copy_name(dentry, target); + /* rehash in new place(s) */ + __d_rehash(dentry); + if (exchange) + __d_rehash(target); + /* ... and switch them in the tree */ if (IS_ROOT(dentry)) { /* splicing a tree */ -- cgit v1.2.3 From e0b3f595d13b3e9ce9cdf53935e7f304c04b5b2b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 29 Jul 2016 18:22:49 -0400 Subject: affs ->d_compare(): don't bother with ->d_inode Use ->d_sb directly. Signed-off-by: Al Viro --- fs/affs/amigaffs.c | 4 +--- fs/affs/namei.c | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/fs/affs/amigaffs.c b/fs/affs/amigaffs.c index d6c7a51c93e4..d8f217c711d3 100644 --- a/fs/affs/amigaffs.c +++ b/fs/affs/amigaffs.c @@ -472,9 +472,7 @@ affs_warning(struct super_block *sb, const char *function, const char *fmt, ...) bool affs_nofilenametruncate(const struct dentry *dentry) { - struct inode *inode = d_inode(dentry); - - return affs_test_opt(AFFS_SB(inode->i_sb)->s_flags, SF_NO_TRUNCATE); + return affs_test_opt(AFFS_SB(dentry->d_sb)->s_flags, SF_NO_TRUNCATE); } /* Check if the name is valid for a affs object. */ diff --git a/fs/affs/namei.c b/fs/affs/namei.c index eb32029bc776..27ca732680d9 100644 --- a/fs/affs/namei.c +++ b/fs/affs/namei.c @@ -136,7 +136,7 @@ affs_compare_dentry(const struct dentry *parent, const struct dentry *dentry, { return __affs_compare_dentry(len, str, name, affs_toupper, - affs_nofilenametruncate(parent)); + affs_nofilenametruncate(dentry)); } static int @@ -144,7 +144,7 @@ affs_intl_compare_dentry(const struct dentry *parent, const struct dentry *dentr unsigned int len, const char *str, const struct qstr *name) { return __affs_compare_dentry(len, str, name, affs_intl_toupper, - affs_nofilenametruncate(parent)); + affs_nofilenametruncate(dentry)); } -- cgit v1.2.3 From d3fe19852e96aabe3084c82ec2f3fb4918ab6d1e Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 29 Jul 2016 18:23:59 -0400 Subject: cifs, msdos, vfat, hfs+: don't bother with parent in ->d_compare() dentry->d_sb is just as good as parent->d_sb Signed-off-by: Al Viro --- fs/cifs/dir.c | 2 +- fs/fat/namei_msdos.c | 2 +- fs/fat/namei_vfat.c | 2 +- fs/hfsplus/unicode.c | 2 +- fs/hpfs/dentry.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 4e532536cbc6..cf394da87cd4 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -906,7 +906,7 @@ static int cifs_ci_hash(const struct dentry *dentry, struct qstr *q) static int cifs_ci_compare(const struct dentry *parent, const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { - struct nls_table *codepage = CIFS_SB(parent->d_sb)->local_nls; + struct nls_table *codepage = CIFS_SB(dentry->d_sb)->local_nls; wchar_t c1, c2; int i, l1, l2; diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c index 1337c0c7527d..0f177127e4d6 100644 --- a/fs/fat/namei_msdos.c +++ b/fs/fat/namei_msdos.c @@ -165,7 +165,7 @@ static int msdos_hash(const struct dentry *dentry, struct qstr *qstr) static int msdos_cmp(const struct dentry *parent, const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { - struct fat_mount_options *options = &MSDOS_SB(parent->d_sb)->options; + struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options; unsigned char a_msdos_name[MSDOS_NAME], b_msdos_name[MSDOS_NAME]; int error; diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c index 6ccdf3f34f90..e60a90841381 100644 --- a/fs/fat/namei_vfat.c +++ b/fs/fat/namei_vfat.c @@ -141,7 +141,7 @@ static int vfat_hashi(const struct dentry *dentry, struct qstr *qstr) static int vfat_cmpi(const struct dentry *parent, const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { - struct nls_table *t = MSDOS_SB(parent->d_sb)->nls_io; + struct nls_table *t = MSDOS_SB(dentry->d_sb)->nls_io; unsigned int alen, blen; /* A filename cannot end in '.' or we treat it like it has none */ diff --git a/fs/hfsplus/unicode.c b/fs/hfsplus/unicode.c index c13c8a240be3..509d7e231460 100644 --- a/fs/hfsplus/unicode.c +++ b/fs/hfsplus/unicode.c @@ -388,7 +388,7 @@ int hfsplus_hash_dentry(const struct dentry *dentry, struct qstr *str) int hfsplus_compare_dentry(const struct dentry *parent, const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { - struct super_block *sb = parent->d_sb; + struct super_block *sb = dentry->d_sb; int casefold, decompose, size; int dsize1, dsize2, len1, len2; const u16 *dstr1, *dstr2; diff --git a/fs/hpfs/dentry.c b/fs/hpfs/dentry.c index 60e6d334d79a..a804300f4dce 100644 --- a/fs/hpfs/dentry.c +++ b/fs/hpfs/dentry.c @@ -50,7 +50,7 @@ static int hpfs_compare_dentry(const struct dentry *parent, const struct dentry if (hpfs_chk_name(name->name, &bl)) return 1; - if (hpfs_compare_names(parent->d_sb, str, al, name->name, bl, 0)) + if (hpfs_compare_names(dentry->d_sb, str, al, name->name, bl, 0)) return 1; return 0; } -- cgit v1.2.3 From 6fa67e707559303e086303aeecc9e8b91ef497d5 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 31 Jul 2016 16:37:25 -0400 Subject: get rid of 'parent' argument of ->d_compare() Signed-off-by: Al Viro --- Documentation/filesystems/Locking | 2 +- Documentation/filesystems/porting | 7 +++++++ Documentation/filesystems/vfs.txt | 2 +- drivers/staging/lustre/lustre/llite/dcache.c | 2 +- fs/adfs/dir.c | 2 +- fs/affs/namei.c | 8 ++++---- fs/cifs/dir.c | 2 +- fs/dcache.c | 4 ++-- fs/efivarfs/super.c | 3 +-- fs/fat/namei_msdos.c | 2 +- fs/fat/namei_vfat.c | 4 ++-- fs/hfs/hfs_fs.h | 2 +- fs/hfs/string.c | 2 +- fs/hfsplus/hfsplus_fs.h | 3 +-- fs/hfsplus/unicode.c | 2 +- fs/hpfs/dentry.c | 2 +- fs/isofs/inode.c | 15 ++++++--------- fs/isofs/namei.c | 2 +- fs/jfs/namei.c | 2 +- fs/ncpfs/dir.c | 6 +++--- fs/proc/proc_sysctl.c | 2 +- include/linux/dcache.h | 2 +- 22 files changed, 40 insertions(+), 38 deletions(-) diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 1b3c39a7de62..d30fb2cb5066 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -12,7 +12,7 @@ prototypes: int (*d_revalidate)(struct dentry *, unsigned int); int (*d_weak_revalidate)(struct dentry *, unsigned int); int (*d_hash)(const struct dentry *, struct qstr *); - int (*d_compare)(const struct dentry *, const struct dentry *, + int (*d_compare)(const struct dentry *, unsigned int, const char *, const struct qstr *); int (*d_delete)(struct dentry *); int (*d_init)(struct dentry *); diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting index a5fb89cac615..b1bd05ea66b2 100644 --- a/Documentation/filesystems/porting +++ b/Documentation/filesystems/porting @@ -585,3 +585,10 @@ in your dentry operations instead. in the instances. Rationale: !@#!@# security_d_instantiate() needs to be called before we attach dentry to inode and !@#!@##!@$!$#!@#$!@$!@$ smack ->d_instantiate() uses not just ->getxattr() but ->setxattr() as well. +-- +[mandatory] + ->d_compare() doesn't get parent as a separate argument anymore. If you + used it for finding the struct super_block involved, dentry->d_sb will + work just as well; if it's something more complicated, use dentry->d_parent. + Just be careful not to assume that fetching it more than once will yield + the same value - in RCU mode it could change under you. diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 8a196851f01d..9ace359d6cc5 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -931,7 +931,7 @@ struct dentry_operations { int (*d_revalidate)(struct dentry *, unsigned int); int (*d_weak_revalidate)(struct dentry *, unsigned int); int (*d_hash)(const struct dentry *, struct qstr *); - int (*d_compare)(const struct dentry *, const struct dentry *, + int (*d_compare)(const struct dentry *, unsigned int, const char *, const struct qstr *); int (*d_delete)(const struct dentry *); int (*d_init)(struct dentry *); diff --git a/drivers/staging/lustre/lustre/llite/dcache.c b/drivers/staging/lustre/lustre/llite/dcache.c index 581a63a0a63e..463b1a360733 100644 --- a/drivers/staging/lustre/lustre/llite/dcache.c +++ b/drivers/staging/lustre/lustre/llite/dcache.c @@ -78,7 +78,7 @@ static void ll_release(struct dentry *de) * INVALID) so d_lookup() matches it, but we have no lock on it (so * lock_match() fails) and we spin around real_lookup(). */ -static int ll_dcompare(const struct dentry *parent, const struct dentry *dentry, +static int ll_dcompare(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c index bec25f7017c0..c30a5a65cbb5 100644 --- a/fs/adfs/dir.c +++ b/fs/adfs/dir.c @@ -227,7 +227,7 @@ adfs_hash(const struct dentry *parent, struct qstr *qstr) * requirements of the underlying filesystem. */ static int -adfs_compare(const struct dentry *parent, const struct dentry *dentry, +adfs_compare(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { int i; diff --git a/fs/affs/namei.c b/fs/affs/namei.c index 27ca732680d9..a2d68f828d53 100644 --- a/fs/affs/namei.c +++ b/fs/affs/namei.c @@ -14,11 +14,11 @@ typedef int (*toupper_t)(int); static int affs_toupper(int ch); static int affs_hash_dentry(const struct dentry *, struct qstr *); -static int affs_compare_dentry(const struct dentry *parent, const struct dentry *dentry, +static int affs_compare_dentry(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name); static int affs_intl_toupper(int ch); static int affs_intl_hash_dentry(const struct dentry *, struct qstr *); -static int affs_intl_compare_dentry(const struct dentry *parent, const struct dentry *dentry, +static int affs_intl_compare_dentry(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name); const struct dentry_operations affs_dentry_operations = { @@ -131,7 +131,7 @@ static inline int __affs_compare_dentry(unsigned int len, } static int -affs_compare_dentry(const struct dentry *parent, const struct dentry *dentry, +affs_compare_dentry(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { @@ -140,7 +140,7 @@ affs_compare_dentry(const struct dentry *parent, const struct dentry *dentry, } static int -affs_intl_compare_dentry(const struct dentry *parent, const struct dentry *dentry, +affs_intl_compare_dentry(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { return __affs_compare_dentry(len, str, name, affs_intl_toupper, diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index cf394da87cd4..4716c54dbfc6 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -903,7 +903,7 @@ static int cifs_ci_hash(const struct dentry *dentry, struct qstr *q) return 0; } -static int cifs_ci_compare(const struct dentry *parent, const struct dentry *dentry, +static int cifs_ci_compare(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { struct nls_table *codepage = CIFS_SB(dentry->d_sb)->local_nls; diff --git a/fs/dcache.c b/fs/dcache.c index d9450bd496ac..32a9209c8138 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2047,7 +2047,7 @@ static inline bool d_same_name(const struct dentry *dentry, return false; return dentry_cmp(dentry, name->name, name->len) == 0; } - return parent->d_op->d_compare(parent, dentry, + return parent->d_op->d_compare(dentry, dentry->d_name.len, dentry->d_name.name, name) == 0; } @@ -2150,7 +2150,7 @@ seqretry: cpu_relax(); goto seqretry; } - if (parent->d_op->d_compare(parent, dentry, + if (parent->d_op->d_compare(dentry, tlen, tname, name) != 0) continue; } else { diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c index a5e607e8f056..688ccc16b702 100644 --- a/fs/efivarfs/super.c +++ b/fs/efivarfs/super.c @@ -45,8 +45,7 @@ static struct super_block *efivarfs_sb; * So we need to perform a case-sensitive match on part 1 and a * case-insensitive match on part 2. */ -static int efivarfs_d_compare(const struct dentry *parent, - const struct dentry *dentry, +static int efivarfs_d_compare(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c index 0f177127e4d6..664655b2c55f 100644 --- a/fs/fat/namei_msdos.c +++ b/fs/fat/namei_msdos.c @@ -162,7 +162,7 @@ static int msdos_hash(const struct dentry *dentry, struct qstr *qstr) * Compare two msdos names. If either of the names are invalid, * we fall back to doing the standard name comparison. */ -static int msdos_cmp(const struct dentry *parent, const struct dentry *dentry, +static int msdos_cmp(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options; diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c index e60a90841381..0335e504e65a 100644 --- a/fs/fat/namei_vfat.c +++ b/fs/fat/namei_vfat.c @@ -138,7 +138,7 @@ static int vfat_hashi(const struct dentry *dentry, struct qstr *qstr) /* * Case insensitive compare of two vfat names. */ -static int vfat_cmpi(const struct dentry *parent, const struct dentry *dentry, +static int vfat_cmpi(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { struct nls_table *t = MSDOS_SB(dentry->d_sb)->nls_io; @@ -157,7 +157,7 @@ static int vfat_cmpi(const struct dentry *parent, const struct dentry *dentry, /* * Case sensitive compare of two vfat names. */ -static int vfat_cmp(const struct dentry *parent, const struct dentry *dentry, +static int vfat_cmp(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { unsigned int alen, blen; diff --git a/fs/hfs/hfs_fs.h b/fs/hfs/hfs_fs.h index ee2f385811c8..f28d7a259105 100644 --- a/fs/hfs/hfs_fs.h +++ b/fs/hfs/hfs_fs.h @@ -233,7 +233,7 @@ extern const struct dentry_operations hfs_dentry_operations; extern int hfs_hash_dentry(const struct dentry *, struct qstr *); extern int hfs_strcmp(const unsigned char *, unsigned int, const unsigned char *, unsigned int); -extern int hfs_compare_dentry(const struct dentry *parent, const struct dentry *dentry, +extern int hfs_compare_dentry(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name); /* trans.c */ diff --git a/fs/hfs/string.c b/fs/hfs/string.c index ec9f164c35a5..3912209153a8 100644 --- a/fs/hfs/string.c +++ b/fs/hfs/string.c @@ -92,7 +92,7 @@ int hfs_strcmp(const unsigned char *s1, unsigned int len1, * Test for equality of two strings in the HFS filename character ordering. * return 1 on failure and 0 on success */ -int hfs_compare_dentry(const struct dentry *parent, const struct dentry *dentry, +int hfs_compare_dentry(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { const unsigned char *n1, *n2; diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index 047245bd2cd6..e95c01f1d62e 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -520,8 +520,7 @@ int hfsplus_uni2asc(struct super_block *sb, const struct hfsplus_unistr *ustr, int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr, int max_unistr_len, const char *astr, int len); int hfsplus_hash_dentry(const struct dentry *dentry, struct qstr *str); -int hfsplus_compare_dentry(const struct dentry *parent, - const struct dentry *dentry, unsigned int len, +int hfsplus_compare_dentry(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name); /* wrapper.c */ diff --git a/fs/hfsplus/unicode.c b/fs/hfsplus/unicode.c index 509d7e231460..e563939882f3 100644 --- a/fs/hfsplus/unicode.c +++ b/fs/hfsplus/unicode.c @@ -385,7 +385,7 @@ int hfsplus_hash_dentry(const struct dentry *dentry, struct qstr *str) * Composed unicode characters are decomposed and case-folding is performed * if the appropriate bits are (un)set on the superblock. */ -int hfsplus_compare_dentry(const struct dentry *parent, const struct dentry *dentry, +int hfsplus_compare_dentry(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { struct super_block *sb = dentry->d_sb; diff --git a/fs/hpfs/dentry.c b/fs/hpfs/dentry.c index a804300f4dce..bb87d65f0d97 100644 --- a/fs/hpfs/dentry.c +++ b/fs/hpfs/dentry.c @@ -34,7 +34,7 @@ static int hpfs_hash_dentry(const struct dentry *dentry, struct qstr *qstr) return 0; } -static int hpfs_compare_dentry(const struct dentry *parent, const struct dentry *dentry, +static int hpfs_compare_dentry(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { unsigned al = len; diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 761fade7680f..ad0c745ebad7 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -29,18 +29,15 @@ #define BEQUIET static int isofs_hashi(const struct dentry *parent, struct qstr *qstr); -static int isofs_dentry_cmpi(const struct dentry *parent, - const struct dentry *dentry, +static int isofs_dentry_cmpi(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name); #ifdef CONFIG_JOLIET static int isofs_hashi_ms(const struct dentry *parent, struct qstr *qstr); static int isofs_hash_ms(const struct dentry *parent, struct qstr *qstr); -static int isofs_dentry_cmpi_ms(const struct dentry *parent, - const struct dentry *dentry, +static int isofs_dentry_cmpi_ms(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name); -static int isofs_dentry_cmp_ms(const struct dentry *parent, - const struct dentry *dentry, +static int isofs_dentry_cmp_ms(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name); #endif @@ -235,7 +232,7 @@ isofs_hashi(const struct dentry *dentry, struct qstr *qstr) } static int -isofs_dentry_cmpi(const struct dentry *parent, const struct dentry *dentry, +isofs_dentry_cmpi(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { return isofs_dentry_cmp_common(len, str, name, 0, 1); @@ -276,14 +273,14 @@ isofs_hashi_ms(const struct dentry *dentry, struct qstr *qstr) } static int -isofs_dentry_cmp_ms(const struct dentry *parent, const struct dentry *dentry, +isofs_dentry_cmp_ms(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { return isofs_dentry_cmp_common(len, str, name, 1, 0); } static int -isofs_dentry_cmpi_ms(const struct dentry *parent, const struct dentry *dentry, +isofs_dentry_cmpi_ms(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { return isofs_dentry_cmp_common(len, str, name, 1, 1); diff --git a/fs/isofs/namei.c b/fs/isofs/namei.c index 7b543e6b6526..aee592767f1d 100644 --- a/fs/isofs/namei.c +++ b/fs/isofs/namei.c @@ -22,7 +22,7 @@ isofs_cmp(struct dentry *dentry, const char *compare, int dlen) qstr.len = dlen; if (likely(!dentry->d_op)) return dentry->d_name.len != dlen || memcmp(dentry->d_name.name, compare, dlen); - return dentry->d_op->d_compare(NULL, NULL, dentry->d_name.len, dentry->d_name.name, &qstr); + return dentry->d_op->d_compare(NULL, dentry->d_name.len, dentry->d_name.name, &qstr); } /* diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c index 04baf0dfc40c..814b0c58016c 100644 --- a/fs/jfs/namei.c +++ b/fs/jfs/namei.c @@ -1572,7 +1572,7 @@ static int jfs_ci_hash(const struct dentry *dir, struct qstr *this) return 0; } -static int jfs_ci_compare(const struct dentry *parent, const struct dentry *dentry, +static int jfs_ci_compare(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { int i, result = 1; diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index 9add7ab747a5..17de5c13dfae 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -74,7 +74,7 @@ const struct inode_operations ncp_dir_inode_operations = */ static int ncp_lookup_validate(struct dentry *, unsigned int); static int ncp_hash_dentry(const struct dentry *, struct qstr *); -static int ncp_compare_dentry(const struct dentry *, const struct dentry *, +static int ncp_compare_dentry(const struct dentry *, unsigned int, const char *, const struct qstr *); static int ncp_delete_dentry(const struct dentry *); static void ncp_d_prune(struct dentry *dentry); @@ -154,7 +154,7 @@ ncp_hash_dentry(const struct dentry *dentry, struct qstr *this) * the callers will handle races. */ static int -ncp_compare_dentry(const struct dentry *parent, const struct dentry *dentry, +ncp_compare_dentry(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { struct inode *pinode; @@ -162,7 +162,7 @@ ncp_compare_dentry(const struct dentry *parent, const struct dentry *dentry, if (len != name->len) return 1; - pinode = d_inode_rcu(parent); + pinode = d_inode_rcu(dentry->d_parent); if (!pinode) return 1; diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index b59db94d2ff4..30bb00130d0f 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -834,7 +834,7 @@ static int sysctl_is_seen(struct ctl_table_header *p) return res; } -static int proc_sys_compare(const struct dentry *parent, const struct dentry *dentry, +static int proc_sys_compare(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { struct ctl_table_header *head; diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 98044a8d1487..107d9abe7166 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -130,7 +130,7 @@ struct dentry_operations { int (*d_revalidate)(struct dentry *, unsigned int); int (*d_weak_revalidate)(struct dentry *, unsigned int); int (*d_hash)(const struct dentry *, struct qstr *); - int (*d_compare)(const struct dentry *, const struct dentry *, + int (*d_compare)(const struct dentry *, unsigned int, const char *, const struct qstr *); int (*d_delete)(const struct dentry *); int (*d_init)(struct dentry *); -- cgit v1.2.3 From c1892c37769cf89c7e7ba57528ae2ccb5d153c9b Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 3 Aug 2016 13:44:27 +0200 Subject: vfs: fix deadlock in file_remove_privs() on overlayfs file_remove_privs() is called with inode lock on file_inode(), which proceeds to calling notify_change() on file->f_path.dentry. Which triggers the WARN_ON_ONCE(!inode_is_locked(inode)) in addition to deadlocking later when ovl_setattr tries to lock the underlying inode again. Fix this mess by not mixing the layers, but doing everything on underlying dentry/inode. Signed-off-by: Miklos Szeredi Fixes: 07a2daab49c5 ("ovl: Copy up underlying inode's ->i_mode to overlay inode") Cc: --- fs/inode.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/inode.c b/fs/inode.c index 4ccbc21b30ce..68db39050446 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1740,8 +1740,8 @@ static int __remove_privs(struct dentry *dentry, int kill) */ int file_remove_privs(struct file *file) { - struct dentry *dentry = file->f_path.dentry; - struct inode *inode = d_inode(dentry); + struct dentry *dentry = file_dentry(file); + struct inode *inode = file_inode(file); int kill; int error = 0; @@ -1749,7 +1749,7 @@ int file_remove_privs(struct file *file) if (IS_NOSEC(inode)) return 0; - kill = file_needs_remove_privs(file); + kill = dentry_needs_remove_privs(dentry); if (kill < 0) return kill; if (kill) -- cgit v1.2.3 From 3e5a4c94a4f88b545d64bbfc7647b06b92c66e98 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 3 Aug 2016 13:44:27 +0200 Subject: vfs: remove file_needs_remove_privs() This function is now unused. Signed-off-by: Miklos Szeredi --- include/linux/fs.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/linux/fs.h b/include/linux/fs.h index dd288148a6b1..3888341913ef 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2726,10 +2726,6 @@ extern void free_inode_nonrcu(struct inode *inode); extern int should_remove_suid(struct dentry *); extern int file_remove_privs(struct file *); extern int dentry_needs_remove_privs(struct dentry *dentry); -static inline int file_needs_remove_privs(struct file *file) -{ - return dentry_needs_remove_privs(file->f_path.dentry); -} extern void __insert_inode_hash(struct inode *, unsigned long hashval); static inline void insert_inode_hash(struct inode *inode) -- cgit v1.2.3 From f0fce87c36aec5c4a895c78b76396f5727047b93 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 3 Aug 2016 13:44:27 +0200 Subject: vfs: make dentry_needs_remove_privs() internal Only used by the vfs. Signed-off-by: Miklos Szeredi --- fs/inode.c | 1 - fs/internal.h | 1 + include/linux/fs.h | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/inode.c b/fs/inode.c index 68db39050446..2029357fcf97 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1720,7 +1720,6 @@ int dentry_needs_remove_privs(struct dentry *dentry) mask |= ATTR_KILL_PRIV; return mask; } -EXPORT_SYMBOL(dentry_needs_remove_privs); static int __remove_privs(struct dentry *dentry, int kill) { diff --git a/fs/internal.h b/fs/internal.h index f57ced528cde..30b568f02613 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -114,6 +114,7 @@ extern int vfs_open(const struct path *, struct file *, const struct cred *); */ extern long prune_icache_sb(struct super_block *sb, struct shrink_control *sc); extern void inode_add_lru(struct inode *inode); +extern int dentry_needs_remove_privs(struct dentry *dentry); /* * fs-writeback.c diff --git a/include/linux/fs.h b/include/linux/fs.h index 3888341913ef..448641bcafc5 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2725,7 +2725,6 @@ extern struct inode *new_inode(struct super_block *sb); extern void free_inode_nonrcu(struct inode *inode); extern int should_remove_suid(struct dentry *); extern int file_remove_privs(struct file *); -extern int dentry_needs_remove_privs(struct dentry *dentry); extern void __insert_inode_hash(struct inode *, unsigned long hashval); static inline void insert_inode_hash(struct inode *inode) -- cgit v1.2.3 From 797fc16d8f00bf681b7f9969c0701725b472b11a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 3 Aug 2016 11:02:48 -0400 Subject: 9p: fix braino introduced in "9p: new helper - v9fs_parent_fid()" In v9fs_vfs_rename() we need to clone the parents' fids, not just find them. Spotted-by: Johannes Berg Signed-off-by: Al Viro --- fs/9p/fid.h | 4 ++++ fs/9p/vfs_inode.c | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/9p/fid.h b/fs/9p/fid.h index 12700df0bb51..d4c5002d56ad 100644 --- a/fs/9p/fid.h +++ b/fs/9p/fid.h @@ -31,4 +31,8 @@ static inline struct p9_fid *v9fs_parent_fid(struct dentry *dentry) struct p9_fid *v9fs_fid_clone(struct dentry *dentry); void v9fs_fid_add(struct dentry *dentry, struct p9_fid *fid); struct p9_fid *v9fs_writeback_fid(struct dentry *dentry); +static inline struct p9_fid *clone_fid(struct p9_fid *fid) +{ + return IS_ERR(fid) ? fid : p9_client_walk(fid, 0, NULL, 1); +} #endif diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 7da9a8354fad..26189eebcd1c 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -975,13 +975,13 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, if (IS_ERR(oldfid)) return PTR_ERR(oldfid); - olddirfid = v9fs_parent_fid(old_dentry); + olddirfid = clone_fid(v9fs_parent_fid(old_dentry)); if (IS_ERR(olddirfid)) { retval = PTR_ERR(olddirfid); goto done; } - newdirfid = v9fs_parent_fid(new_dentry); + newdirfid = clone_fid(v9fs_parent_fid(new_dentry)); if (IS_ERR(newdirfid)) { retval = PTR_ERR(newdirfid); goto clunk_olddir; -- cgit v1.2.3 From 7d50a29fe43848b7d3c8309f8617db3305c0db18 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 3 Aug 2016 11:12:12 -0400 Subject: 9p: use clone_fid() in a bunch of places it cleans the things up Signed-off-by: Al Viro --- fs/9p/fid.c | 26 +------------------------- fs/9p/fid.h | 5 ++++- fs/9p/vfs_inode.c | 2 +- fs/9p/vfs_inode_dotl.c | 2 +- fs/9p/xattr.c | 4 +--- 5 files changed, 8 insertions(+), 31 deletions(-) diff --git a/fs/9p/fid.c b/fs/9p/fid.c index 47db55aee7f2..60fb47469c86 100644 --- a/fs/9p/fid.c +++ b/fs/9p/fid.c @@ -257,36 +257,12 @@ struct p9_fid *v9fs_fid_lookup(struct dentry *dentry) return v9fs_fid_lookup_with_uid(dentry, uid, any); } -struct p9_fid *v9fs_fid_clone(struct dentry *dentry) -{ - struct p9_fid *fid, *ret; - - fid = v9fs_fid_lookup(dentry); - if (IS_ERR(fid)) - return fid; - - ret = p9_client_walk(fid, 0, NULL, 1); - return ret; -} - -static struct p9_fid *v9fs_fid_clone_with_uid(struct dentry *dentry, kuid_t uid) -{ - struct p9_fid *fid, *ret; - - fid = v9fs_fid_lookup_with_uid(dentry, uid, 0); - if (IS_ERR(fid)) - return fid; - - ret = p9_client_walk(fid, 0, NULL, 1); - return ret; -} - struct p9_fid *v9fs_writeback_fid(struct dentry *dentry) { int err; struct p9_fid *fid; - fid = v9fs_fid_clone_with_uid(dentry, GLOBAL_ROOT_UID); + fid = clone_fid(v9fs_fid_lookup_with_uid(dentry, GLOBAL_ROOT_UID, 0)); if (IS_ERR(fid)) goto error_out; /* diff --git a/fs/9p/fid.h b/fs/9p/fid.h index d4c5002d56ad..4491bcaf42b8 100644 --- a/fs/9p/fid.h +++ b/fs/9p/fid.h @@ -28,11 +28,14 @@ static inline struct p9_fid *v9fs_parent_fid(struct dentry *dentry) { return v9fs_fid_lookup(dentry->d_parent); } -struct p9_fid *v9fs_fid_clone(struct dentry *dentry); void v9fs_fid_add(struct dentry *dentry, struct p9_fid *fid); struct p9_fid *v9fs_writeback_fid(struct dentry *dentry); static inline struct p9_fid *clone_fid(struct p9_fid *fid) { return IS_ERR(fid) ? fid : p9_client_walk(fid, 0, NULL, 1); } +static inline struct p9_fid *v9fs_fid_clone(struct dentry *dentry) +{ + return clone_fid(v9fs_fid_lookup(dentry)); +} #endif diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 26189eebcd1c..8b1999b528e9 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -661,7 +661,7 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir, } /* clone a fid to use for creation */ - ofid = p9_client_walk(dfid, 0, NULL, 1); + ofid = clone_fid(dfid); if (IS_ERR(ofid)) { err = PTR_ERR(ofid); p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err); diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index 2ed04c2fe7af..eeabcb0bad12 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -281,7 +281,7 @@ v9fs_vfs_atomic_open_dotl(struct inode *dir, struct dentry *dentry, } /* clone a fid to use for creation */ - ofid = p9_client_walk(dfid, 0, NULL, 1); + ofid = clone_fid(dfid); if (IS_ERR(ofid)) { err = PTR_ERR(ofid); p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err); diff --git a/fs/9p/xattr.c b/fs/9p/xattr.c index a6bd349bab23..f329eee6dc93 100644 --- a/fs/9p/xattr.c +++ b/fs/9p/xattr.c @@ -97,8 +97,6 @@ int v9fs_xattr_set(struct dentry *dentry, const char *name, const void *value, size_t value_len, int flags) { struct p9_fid *fid = v9fs_fid_lookup(dentry); - if (IS_ERR(fid)) - return PTR_ERR(fid); return v9fs_fid_xattr_set(fid, name, value, value_len, flags); } @@ -115,7 +113,7 @@ int v9fs_fid_xattr_set(struct p9_fid *fid, const char *name, name, value_len, flags); /* Clone it */ - fid = p9_client_walk(fid, 0, NULL, 1); + fid = clone_fid(fid); if (IS_ERR(fid)) return PTR_ERR(fid); -- cgit v1.2.3 From db20a8925bc420eed033c5d91ff6afa74465e521 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 3 Aug 2016 17:57:39 +0100 Subject: cachefiles: Fix race between inactivating and culling a cache object There's a race between cachefiles_mark_object_inactive() and cachefiles_cull(): (1) cachefiles_cull() can't delete a backing file until the cache object is marked inactive, but as soon as that's the case it's fair game. (2) cachefiles_mark_object_inactive() marks the object as being inactive and *only then* reads the i_blocks on the backing inode - but cachefiles_cull() might've managed to delete it by this point. Fix this by making sure cachefiles_mark_object_inactive() gets any data it needs from the backing inode before deactivating the object. Without this, the following oops may occur: BUG: unable to handle kernel NULL pointer dereference at 0000000000000098 IP: [] cachefiles_mark_object_inactive+0x61/0xb0 [cachefiles] ... CPU: 11 PID: 527 Comm: kworker/u64:4 Tainted: G I ------------ 3.10.0-470.el7.x86_64 #1 Hardware name: Hewlett-Packard HP Z600 Workstation/0B54h, BIOS 786G4 v03.19 03/11/2011 Workqueue: fscache_object fscache_object_work_func [fscache] task: ffff880035edaf10 ti: ffff8800b77c0000 task.ti: ffff8800b77c0000 RIP: 0010:[] cachefiles_mark_object_inactive+0x61/0xb0 [cachefiles] RSP: 0018:ffff8800b77c3d70 EFLAGS: 00010246 RAX: 0000000000000000 RBX: ffff8800bf6cc400 RCX: 0000000000000034 RDX: 0000000000000000 RSI: ffff880090ffc710 RDI: ffff8800bf761ef8 RBP: ffff8800b77c3d88 R08: 2000000000000000 R09: 0090ffc710000000 R10: ff51005d2ff1c400 R11: 0000000000000000 R12: ffff880090ffc600 R13: ffff8800bf6cc520 R14: ffff8800bf6cc400 R15: ffff8800bf6cc498 FS: 0000000000000000(0000) GS:ffff8800bb8c0000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 0000000000000098 CR3: 00000000019ba000 CR4: 00000000000007e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Stack: ffff880090ffc600 ffff8800bf6cc400 ffff8800867df140 ffff8800b77c3db0 ffffffffa06c48cb ffff880090ffc600 ffff880090ffc180 ffff880090ffc658 ffff8800b77c3df0 ffffffffa085d846 ffff8800a96b8150 ffff880090ffc600 Call Trace: [] cachefiles_drop_object+0x6b/0xf0 [cachefiles] [] fscache_drop_object+0xd6/0x1e0 [fscache] [] fscache_object_work_func+0xa5/0x200 [fscache] [] process_one_work+0x17b/0x470 [] worker_thread+0x126/0x410 [] ? rescuer_thread+0x460/0x460 [] kthread+0xcf/0xe0 [] ? kthread_create_on_node+0x140/0x140 [] ret_from_fork+0x58/0x90 [] ? kthread_create_on_node+0x140/0x140 The oopsing code shows: callq 0xffffffff810af6a0 mov 0xf8(%r12),%rax mov 0x30(%rax),%rax mov 0x98(%rax),%rax <---- oops here lock add %rax,0x130(%rbx) where this is: d_backing_inode(object->dentry)->i_blocks Fixes: a5b3a80b899bda0f456f1246c4c5a1191ea01519 (CacheFiles: Provide read-and-reset release counters for cachefilesd) Reported-by: Jianhong Yin Signed-off-by: David Howells Reviewed-by: Jeff Layton Reviewed-by: Steve Dickson cc: stable@vger.kernel.org Signed-off-by: Al Viro --- fs/cachefiles/namei.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index 4ae75006e73b..3f7c2cd41f8f 100644 --- a/fs/cachefiles/namei.c +++ b/fs/cachefiles/namei.c @@ -263,6 +263,8 @@ requeue: void cachefiles_mark_object_inactive(struct cachefiles_cache *cache, struct cachefiles_object *object) { + blkcnt_t i_blocks = d_backing_inode(object->dentry)->i_blocks; + write_lock(&cache->active_lock); rb_erase(&object->active_node, &cache->active_nodes); clear_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags); @@ -273,8 +275,7 @@ void cachefiles_mark_object_inactive(struct cachefiles_cache *cache, /* This object can now be culled, so we need to let the daemon know * that there is something it can remove if it needs to. */ - atomic_long_add(d_backing_inode(object->dentry)->i_blocks, - &cache->b_released); + atomic_long_add(i_blocks, &cache->b_released); if (atomic_inc_return(&cache->f_released)) cachefiles_state_changed(cache); } -- cgit v1.2.3