make it easier to catch those who try to modify ->d_name

Turn d_name into an anon union of const struct qstr d_name with
struct qstr __d_name.  Very few places need to modify it (all
in fs/dcache.c); those are switched to use of ->__d_name.

Note that ->d_name can actually change under you unless you have
the right locking environment; this const just prohibits accidentally
doing stores without being easily spotted.

Reviewed-by: Christian Brauner <brauner@kernel.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
Al Viro 2024-02-09 14:57:43 -05:00
parent ca97d6c60b
commit 180a9cc3fd
2 changed files with 17 additions and 14 deletions

View file

@ -1717,13 +1717,13 @@ static struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
dname = dentry->d_shortname.string;
}
dentry->d_name.len = name->len;
dentry->d_name.hash = name->hash;
dentry->__d_name.len = name->len;
dentry->__d_name.hash = name->hash;
memcpy(dname, name->name, name->len);
dname[name->len] = 0;
/* Make sure we always see the terminating NUL character */
smp_store_release(&dentry->d_name.name, dname); /* ^^^ */
smp_store_release(&dentry->__d_name.name, dname); /* ^^^ */
dentry->d_flags = 0;
lockref_init(&dentry->d_lockref);
@ -2743,15 +2743,15 @@ static void swap_names(struct dentry *dentry, struct dentry *target)
/*
* Both external: swap the pointers
*/
swap(target->d_name.name, dentry->d_name.name);
swap(target->__d_name.name, dentry->__d_name.name);
} else {
/*
* dentry:internal, target:external. Steal target's
* storage and make target internal.
*/
dentry->d_name.name = target->d_name.name;
dentry->__d_name.name = target->__d_name.name;
target->d_shortname = dentry->d_shortname;
target->d_name.name = target->d_shortname.string;
target->__d_name.name = target->d_shortname.string;
}
} else {
if (unlikely(dname_external(dentry))) {
@ -2759,9 +2759,9 @@ static void swap_names(struct dentry *dentry, struct dentry *target)
* dentry:external, target:internal. Give dentry's
* storage to target and make dentry internal
*/
target->d_name.name = dentry->d_name.name;
target->__d_name.name = dentry->__d_name.name;
dentry->d_shortname = target->d_shortname;
dentry->d_name.name = dentry->d_shortname.string;
dentry->__d_name.name = dentry->d_shortname.string;
} else {
/*
* Both are internal.
@ -2771,7 +2771,7 @@ static void swap_names(struct dentry *dentry, struct dentry *target)
target->d_shortname.words[i]);
}
}
swap(dentry->d_name.hash_len, target->d_name.hash_len);
swap(dentry->__d_name.hash_len, target->__d_name.hash_len);
}
static void copy_name(struct dentry *dentry, struct dentry *target)
@ -2781,11 +2781,11 @@ static void copy_name(struct dentry *dentry, struct dentry *target)
old_name = external_name(dentry);
if (unlikely(dname_external(target))) {
atomic_inc(&external_name(target)->count);
dentry->d_name = target->d_name;
dentry->__d_name = target->__d_name;
} else {
dentry->d_shortname = target->d_shortname;
dentry->d_name.name = dentry->d_shortname.string;
dentry->d_name.hash_len = target->d_name.hash_len;
dentry->__d_name.name = dentry->d_shortname.string;
dentry->__d_name.hash_len = target->__d_name.hash_len;
}
if (old_name && likely(atomic_dec_and_test(&old_name->count)))
kfree_rcu(old_name, head);
@ -3133,7 +3133,7 @@ void d_mark_tmpfile(struct file *file, struct inode *inode)
!d_unlinked(dentry));
spin_lock(&dentry->d_parent->d_lock);
spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
dentry->d_name.len = sprintf(dentry->d_shortname.string, "#%llu",
dentry->__d_name.len = sprintf(dentry->d_shortname.string, "#%llu",
(unsigned long long)inode->i_ino);
spin_unlock(&dentry->d_lock);
spin_unlock(&dentry->d_parent->d_lock);

View file

@ -95,7 +95,10 @@ struct dentry {
seqcount_spinlock_t d_seq; /* per dentry seqlock */
struct hlist_bl_node d_hash; /* lookup hash list */
struct dentry *d_parent; /* parent directory */
struct qstr d_name;
union {
struct qstr __d_name; /* for use ONLY in fs/dcache.c */
const struct qstr d_name;
};
struct inode *d_inode; /* Where the name belongs to - NULL is
* negative */
union shortname_store d_shortname;