2
0
mirror of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git synced 2025-09-04 20:19:47 +08:00

Current exclusion rules for ->d_flags stores are rather unpleasant.

The basic rules are simple:
 	* stores to dentry->d_flags are OK under dentry->d_lock.
 	* stores to dentry->d_flags are OK in the dentry constructor, before
 becomes potentially visible to other threads.
 Unfortunately, there's a couple of exceptions to that, and that's where the
 headache comes from.
 
 	Main PITA comes from d_set_d_op(); that primitive sets ->d_op
 of dentry and adjusts the flags that correspond to presence of individual
 methods.  It's very easy to misuse; existing uses _are_ safe, but proof
 of correctness is brittle.
 
 	Use in __d_alloc() is safe (we are within a constructor), but we
 might as well precalculate the initial value of ->d_flags when we set
 the default ->d_op for given superblock and set ->d_flags directly
 instead of messing with that helper.
 
 	The reasons why other uses are safe are bloody convoluted; I'm not going
 to reproduce it here.  See https://lore.kernel.org/all/20250224010624.GT1977892@ZenIV/
 for gory details, if you care.  The critical part is using d_set_d_op() only
 just prior to d_splice_alias(), which makes a combination of d_splice_alias()
 with setting ->d_op, etc. a natural replacement primitive.  Better yet, if
 we go that way, it's easy to take setting ->d_op and modifying ->d_flags
 under ->d_lock, which eliminates the headache as far as ->d_flags exclusion
 rules are concerned.  Other exceptions are minor and easy to deal with.
 
 	What this series does:
 * d_set_d_op() is no longer available; new primitive (d_splice_alias_ops())
 is provided, equivalent to combination of d_set_d_op() and d_splice_alias().
 * new field of struct super_block - ->s_d_flags.  Default value of ->d_flags
 to be used when allocating dentries on this filesystem.
 * new primitive for setting ->s_d_op: set_default_d_op().  Replaces stores
 to ->s_d_op at mount time.  All in-tree filesystems converted; out-of-tree
 ones will get caught by compiler (->s_d_op is renamed, so stores to it will
 be caught).  ->s_d_flags is set by the same primitive to match the ->s_d_op.
 * a lot of filesystems had ->s_d_op->d_delete equal to always_delete_dentry;
 that is equivalent to setting DCACHE_DONTCACHE in ->d_flags, so such filesystems
 can bloody well set that bit in ->s_d_flags and drop ->d_delete() from
 dentry_operations.  In quite a few cases that results in empty dentry_operations,
 which means that we can get rid of those.
 * kill simple_dentry_operations - not needed anymore.
 * massage d_alloc_parallel() to get rid of the other exception wrt ->d_flags
 stores - we can set DCACHE_PAR_LOOKUP as soon as we allocate the new dentry;
 no need to delay that until we commit to using the sucker.
 
 As the result, ->d_flags stores are all either under ->d_lock or done before
 the dentry becomes visible in any shared data structures.
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQQqUNBr3gm4hGXdBJlZ7Krx/gZQ6wUCaIQ/tQAKCRBZ7Krx/gZQ
 66AhAQDgQ+S224x5YevNXc9mDoGUBMF4OG0n0fIla9rfdL4I6wEAqpOWMNDcVPCZ
 GwYOvJ9YuqNdz+MyprAI18Yza4GOmgs=
 =rTYB
 -----END PGP SIGNATURE-----

Merge tag 'pull-dcache' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Pull dentry d_flags updates from Al Viro:
 "The current exclusion rules for dentry->d_flags stores are rather
  unpleasant. The basic rules are simple:

   - stores to dentry->d_flags are OK under dentry->d_lock

   - stores to dentry->d_flags are OK in the dentry constructor, before
     becomes potentially visible to other threads

  Unfortunately, there's a couple of exceptions to that, and that's
  where the headache comes from.

  The main PITA comes from d_set_d_op(); that primitive sets ->d_op of
  dentry and adjusts the flags that correspond to presence of individual
  methods. It's very easy to misuse; existing uses _are_ safe, but proof
  of correctness is brittle.

  Use in __d_alloc() is safe (we are within a constructor), but we might
  as well precalculate the initial value of 'd_flags' when we set the
  default ->d_op for given superblock and set 'd_flags' directly instead
  of messing with that helper.

  The reasons why other uses are safe are bloody convoluted; I'm not
  going to reproduce it here. See [1] for gory details, if you care. The
  critical part is using d_set_d_op() only just prior to
  d_splice_alias(), which makes a combination of d_splice_alias() with
  setting ->d_op, etc a natural replacement primitive.

  Better yet, if we go that way, it's easy to take setting ->d_op and
  modifying 'd_flags' under ->d_lock, which eliminates the headache as
  far as 'd_flags' exclusion rules are concerned. Other exceptions are
  minor and easy to deal with.

  What this series does:

   - d_set_d_op() is no longer available; instead a new primitive
     (d_splice_alias_ops()) is provided, equivalent to combination of
     d_set_d_op() and d_splice_alias().

   - new field of struct super_block - 's_d_flags'. This sets the
     default value of 'd_flags' to be used when allocating dentries on
     this filesystem.

   - new primitive for setting 's_d_op': set_default_d_op(). This
     replaces stores to 's_d_op' at mount time.

     All in-tree filesystems converted; out-of-tree ones will get caught
     by the compiler ('s_d_op' is renamed, so stores to it will be
     caught). 's_d_flags' is set by the same primitive to match the
     's_d_op'.

   - a lot of filesystems had sb->s_d_op->d_delete equal to
     always_delete_dentry; that is equivalent to setting
     DCACHE_DONTCACHE in 'd_flags', so such filesystems can bloody well
     set that bit in 's_d_flags' and drop 'd_delete()' from
     dentry_operations.

     In quite a few cases that results in empty dentry_operations, which
     means that we can get rid of those.

   - kill simple_dentry_operations - not needed anymore

   - massage d_alloc_parallel() to get rid of the other exception wrt
     'd_flags' stores - we can set DCACHE_PAR_LOOKUP as soon as we
     allocate the new dentry; no need to delay that until we commit to
     using the sucker.

  As the result, 'd_flags' stores are all either under ->d_lock or done
  before the dentry becomes visible in any shared data structures"

Link: https://lore.kernel.org/all/20250224010624.GT1977892@ZenIV/ [1]

* tag 'pull-dcache' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (21 commits)
  configfs: use DCACHE_DONTCACHE
  debugfs: use DCACHE_DONTCACHE
  efivarfs: use DCACHE_DONTCACHE instead of always_delete_dentry()
  9p: don't bother with always_delete_dentry
  ramfs, hugetlbfs, mqueue: set DCACHE_DONTCACHE
  kill simple_dentry_operations
  devpts, sunrpc, hostfs: don't bother with ->d_op
  shmem: no dentry retention past the refcount reaching zero
  d_alloc_parallel(): set DCACHE_PAR_LOOKUP earlier
  make d_set_d_op() static
  simple_lookup(): just set DCACHE_DONTCACHE
  tracefs: Add d_delete to remove negative dentries
  set_default_d_op(): calculate the matching value for ->d_flags
  correct the set of flags forbidden at d_set_d_op() time
  split d_flags calculation out of d_set_d_op()
  new helper: set_default_d_op()
  fuse: no need for special dentry_operations for root dentry
  switch procfs from d_set_d_op() to d_splice_alias_ops()
  new helper: d_splice_alias_ops()
  procfs: kill ->proc_dops
  ...
This commit is contained in:
Linus Torvalds 2025-07-28 09:17:57 -07:00
commit 11fe69fbd5
53 changed files with 211 additions and 160 deletions

View File

@ -1258,3 +1258,21 @@ iterator needed. Instead of a cloned mount tree, the new interface returns
an array of struct path, one for each mount collect_mounts() would've an array of struct path, one for each mount collect_mounts() would've
created. These struct path point to locations in the caller's namespace created. These struct path point to locations in the caller's namespace
that would be roots of the cloned mounts. that would be roots of the cloned mounts.
---
**mandatory**
If your filesystem sets the default dentry_operations, use set_default_d_op()
rather than manually setting sb->s_d_op.
---
**mandatory**
d_set_d_op() is no longer exported (or public, for that matter); _if_
your filesystem really needed that, make use of d_splice_alias_ops()
to have them set. Better yet, think hard whether you need different
->d_op for different dentries - if not, just use set_default_d_op()
at mount time and be done with that. Currently procfs is the only
thing that really needs ->d_op varying between dentries.

View File

@ -127,7 +127,6 @@ const struct dentry_operations v9fs_cached_dentry_operations = {
}; };
const struct dentry_operations v9fs_dentry_operations = { const struct dentry_operations v9fs_dentry_operations = {
.d_delete = always_delete_dentry,
.d_release = v9fs_dentry_release, .d_release = v9fs_dentry_release,
.d_unalias_trylock = v9fs_dentry_unalias_trylock, .d_unalias_trylock = v9fs_dentry_unalias_trylock,
.d_unalias_unlock = v9fs_dentry_unalias_unlock, .d_unalias_unlock = v9fs_dentry_unalias_unlock,

View File

@ -134,10 +134,12 @@ static struct dentry *v9fs_mount(struct file_system_type *fs_type, int flags,
if (retval) if (retval)
goto release_sb; goto release_sb;
if (v9ses->cache & (CACHE_META|CACHE_LOOSE)) if (v9ses->cache & (CACHE_META|CACHE_LOOSE)) {
sb->s_d_op = &v9fs_cached_dentry_operations; set_default_d_op(sb, &v9fs_cached_dentry_operations);
else } else {
sb->s_d_op = &v9fs_dentry_operations; set_default_d_op(sb, &v9fs_dentry_operations);
sb->s_d_flags |= DCACHE_DONTCACHE;
}
inode = v9fs_get_new_inode_from_fid(v9ses, fid, sb); inode = v9fs_get_new_inode_from_fid(v9ses, fid, sb);
if (IS_ERR(inode)) { if (IS_ERR(inode)) {

View File

@ -397,7 +397,7 @@ static int adfs_fill_super(struct super_block *sb, struct fs_context *fc)
if (asb->s_ftsuffix) if (asb->s_ftsuffix)
asb->s_namelen += 4; asb->s_namelen += 4;
sb->s_d_op = &adfs_dentry_operations; set_default_d_op(sb, &adfs_dentry_operations);
root = adfs_iget(sb, &root_obj); root = adfs_iget(sb, &root_obj);
sb->s_root = d_make_root(root); sb->s_root = d_make_root(root);
if (!sb->s_root) { if (!sb->s_root) {

View File

@ -500,9 +500,9 @@ got_root:
return PTR_ERR(root_inode); return PTR_ERR(root_inode);
if (affs_test_opt(AFFS_SB(sb)->s_flags, SF_INTL)) if (affs_test_opt(AFFS_SB(sb)->s_flags, SF_INTL))
sb->s_d_op = &affs_intl_dentry_operations; set_default_d_op(sb, &affs_intl_dentry_operations);
else else
sb->s_d_op = &affs_dentry_operations; set_default_d_op(sb, &affs_dentry_operations);
sb->s_root = d_make_root(root_inode); sb->s_root = d_make_root(root_inode);
if (!sb->s_root) { if (!sb->s_root) {

View File

@ -483,9 +483,9 @@ static int afs_fill_super(struct super_block *sb, struct afs_fs_context *ctx)
goto error; goto error;
if (as->dyn_root) { if (as->dyn_root) {
sb->s_d_op = &afs_dynroot_dentry_operations; set_default_d_op(sb, &afs_dynroot_dentry_operations);
} else { } else {
sb->s_d_op = &afs_fs_dentry_operations; set_default_d_op(sb, &afs_fs_dentry_operations);
rcu_assign_pointer(as->volume->sb, sb); rcu_assign_pointer(as->volume->sb, sb);
} }

View File

@ -311,7 +311,7 @@ static int autofs_fill_super(struct super_block *s, struct fs_context *fc)
s->s_blocksize_bits = 10; s->s_blocksize_bits = 10;
s->s_magic = AUTOFS_SUPER_MAGIC; s->s_magic = AUTOFS_SUPER_MAGIC;
s->s_op = &autofs_sops; s->s_op = &autofs_sops;
s->s_d_op = &autofs_dentry_operations; set_default_d_op(s, &autofs_dentry_operations);
s->s_time_gran = 1; s->s_time_gran = 1;
/* /*

View File

@ -959,7 +959,7 @@ static int btrfs_fill_super(struct super_block *sb,
sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_magic = BTRFS_SUPER_MAGIC; sb->s_magic = BTRFS_SUPER_MAGIC;
sb->s_op = &btrfs_super_ops; sb->s_op = &btrfs_super_ops;
sb->s_d_op = &btrfs_dentry_operations; set_default_d_op(sb, &btrfs_dentry_operations);
sb->s_export_op = &btrfs_export_ops; sb->s_export_op = &btrfs_export_ops;
#ifdef CONFIG_FS_VERITY #ifdef CONFIG_FS_VERITY
sb->s_vop = &btrfs_verityops; sb->s_vop = &btrfs_verityops;

View File

@ -1219,7 +1219,7 @@ static int ceph_set_super(struct super_block *s, struct fs_context *fc)
fsc->max_file_size = 1ULL << 40; /* temp value until we get mdsmap */ fsc->max_file_size = 1ULL << 40; /* temp value until we get mdsmap */
s->s_op = &ceph_super_ops; s->s_op = &ceph_super_ops;
s->s_d_op = &ceph_dentry_ops; set_default_d_op(s, &ceph_dentry_ops);
s->s_export_op = &ceph_export_ops; s->s_export_op = &ceph_export_ops;
s->s_time_gran = 1; s->s_time_gran = 1;

View File

@ -230,7 +230,7 @@ static int coda_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_blocksize_bits = 12; sb->s_blocksize_bits = 12;
sb->s_magic = CODA_SUPER_MAGIC; sb->s_magic = CODA_SUPER_MAGIC;
sb->s_op = &coda_super_operations; sb->s_op = &coda_super_operations;
sb->s_d_op = &coda_dentry_operations; set_default_d_op(sb, &coda_dentry_operations);
sb->s_time_gran = 1; sb->s_time_gran = 1;
sb->s_time_min = S64_MIN; sb->s_time_min = S64_MIN;
sb->s_time_max = S64_MAX; sb->s_time_max = S64_MAX;

View File

@ -67,7 +67,6 @@ static void configfs_d_iput(struct dentry * dentry,
const struct dentry_operations configfs_dentry_ops = { const struct dentry_operations configfs_dentry_ops = {
.d_iput = configfs_d_iput, .d_iput = configfs_d_iput,
.d_delete = always_delete_dentry,
}; };
#ifdef CONFIG_LOCKDEP #ifdef CONFIG_LOCKDEP

View File

@ -92,7 +92,8 @@ static int configfs_fill_super(struct super_block *sb, struct fs_context *fc)
configfs_root_group.cg_item.ci_dentry = root; configfs_root_group.cg_item.ci_dentry = root;
root->d_fsdata = &configfs_root; root->d_fsdata = &configfs_root;
sb->s_root = root; sb->s_root = root;
sb->s_d_op = &configfs_dentry_ops; /* the rest get that */ set_default_d_op(sb, &configfs_dentry_ops); /* the rest get that */
sb->s_d_flags |= DCACHE_DONTCACHE;
return 0; return 0;
} }

View File

@ -1436,7 +1436,7 @@ int d_set_mounted(struct dentry *dentry)
{ {
struct dentry *p; struct dentry *p;
int ret = -ENOENT; int ret = -ENOENT;
write_seqlock(&rename_lock); read_seqlock_excl(&rename_lock);
for (p = dentry->d_parent; !IS_ROOT(p); p = p->d_parent) { for (p = dentry->d_parent; !IS_ROOT(p); p = p->d_parent) {
/* Need exclusion wrt. d_invalidate() */ /* Need exclusion wrt. d_invalidate() */
spin_lock(&p->d_lock); spin_lock(&p->d_lock);
@ -1456,7 +1456,7 @@ int d_set_mounted(struct dentry *dentry)
} }
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
out: out:
write_sequnlock(&rename_lock); read_sequnlock_excl(&rename_lock);
return ret; return ret;
} }
@ -1731,14 +1731,14 @@ static struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
dentry->d_inode = NULL; dentry->d_inode = NULL;
dentry->d_parent = dentry; dentry->d_parent = dentry;
dentry->d_sb = sb; dentry->d_sb = sb;
dentry->d_op = NULL; dentry->d_op = sb->__s_d_op;
dentry->d_flags = sb->s_d_flags;
dentry->d_fsdata = NULL; dentry->d_fsdata = NULL;
INIT_HLIST_BL_NODE(&dentry->d_hash); INIT_HLIST_BL_NODE(&dentry->d_hash);
INIT_LIST_HEAD(&dentry->d_lru); INIT_LIST_HEAD(&dentry->d_lru);
INIT_HLIST_HEAD(&dentry->d_children); INIT_HLIST_HEAD(&dentry->d_children);
INIT_HLIST_NODE(&dentry->d_u.d_alias); INIT_HLIST_NODE(&dentry->d_u.d_alias);
INIT_HLIST_NODE(&dentry->d_sib); INIT_HLIST_NODE(&dentry->d_sib);
d_set_d_op(dentry, dentry->d_sb->s_d_op);
if (dentry->d_op && dentry->d_op->d_init) { if (dentry->d_op && dentry->d_op->d_init) {
err = dentry->d_op->d_init(dentry); err = dentry->d_op->d_init(dentry);
@ -1821,8 +1821,9 @@ struct dentry *d_alloc_pseudo(struct super_block *sb, const struct qstr *name)
struct dentry *dentry = __d_alloc(sb, name); struct dentry *dentry = __d_alloc(sb, name);
if (likely(dentry)) { if (likely(dentry)) {
dentry->d_flags |= DCACHE_NORCU; dentry->d_flags |= DCACHE_NORCU;
if (!sb->s_d_op) /* d_op_flags(&anon_ops) is 0 */
d_set_d_op(dentry, &anon_ops); if (!dentry->d_op)
dentry->d_op = &anon_ops;
} }
return dentry; return dentry;
} }
@ -1837,35 +1838,50 @@ struct dentry *d_alloc_name(struct dentry *parent, const char *name)
} }
EXPORT_SYMBOL(d_alloc_name); EXPORT_SYMBOL(d_alloc_name);
void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op) #define DCACHE_OP_FLAGS \
{ (DCACHE_OP_HASH | DCACHE_OP_COMPARE | DCACHE_OP_REVALIDATE | \
WARN_ON_ONCE(dentry->d_op); DCACHE_OP_WEAK_REVALIDATE | DCACHE_OP_DELETE | DCACHE_OP_PRUNE | \
WARN_ON_ONCE(dentry->d_flags & (DCACHE_OP_HASH | DCACHE_OP_REAL)
DCACHE_OP_COMPARE |
DCACHE_OP_REVALIDATE |
DCACHE_OP_WEAK_REVALIDATE |
DCACHE_OP_DELETE |
DCACHE_OP_REAL));
dentry->d_op = op;
if (!op)
return;
if (op->d_hash)
dentry->d_flags |= DCACHE_OP_HASH;
if (op->d_compare)
dentry->d_flags |= DCACHE_OP_COMPARE;
if (op->d_revalidate)
dentry->d_flags |= DCACHE_OP_REVALIDATE;
if (op->d_weak_revalidate)
dentry->d_flags |= DCACHE_OP_WEAK_REVALIDATE;
if (op->d_delete)
dentry->d_flags |= DCACHE_OP_DELETE;
if (op->d_prune)
dentry->d_flags |= DCACHE_OP_PRUNE;
if (op->d_real)
dentry->d_flags |= DCACHE_OP_REAL;
static unsigned int d_op_flags(const struct dentry_operations *op)
{
unsigned int flags = 0;
if (op) {
if (op->d_hash)
flags |= DCACHE_OP_HASH;
if (op->d_compare)
flags |= DCACHE_OP_COMPARE;
if (op->d_revalidate)
flags |= DCACHE_OP_REVALIDATE;
if (op->d_weak_revalidate)
flags |= DCACHE_OP_WEAK_REVALIDATE;
if (op->d_delete)
flags |= DCACHE_OP_DELETE;
if (op->d_prune)
flags |= DCACHE_OP_PRUNE;
if (op->d_real)
flags |= DCACHE_OP_REAL;
} }
EXPORT_SYMBOL(d_set_d_op); return flags;
}
static void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op)
{
unsigned int flags = d_op_flags(op);
WARN_ON_ONCE(dentry->d_op);
WARN_ON_ONCE(dentry->d_flags & DCACHE_OP_FLAGS);
dentry->d_op = op;
if (flags)
dentry->d_flags |= flags;
}
void set_default_d_op(struct super_block *s, const struct dentry_operations *ops)
{
unsigned int flags = d_op_flags(ops);
s->__s_d_op = ops;
s->s_d_flags = (s->s_d_flags & ~DCACHE_OP_FLAGS) | flags;
}
EXPORT_SYMBOL(set_default_d_op);
static unsigned d_flags_for_inode(struct inode *inode) static unsigned d_flags_for_inode(struct inode *inode)
{ {
@ -2530,13 +2546,19 @@ struct dentry *d_alloc_parallel(struct dentry *parent,
unsigned int hash = name->hash; unsigned int hash = name->hash;
struct hlist_bl_head *b = in_lookup_hash(parent, hash); struct hlist_bl_head *b = in_lookup_hash(parent, hash);
struct hlist_bl_node *node; struct hlist_bl_node *node;
struct dentry *new = d_alloc(parent, name); struct dentry *new = __d_alloc(parent->d_sb, name);
struct dentry *dentry; struct dentry *dentry;
unsigned seq, r_seq, d_seq; unsigned seq, r_seq, d_seq;
if (unlikely(!new)) if (unlikely(!new))
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
new->d_flags |= DCACHE_PAR_LOOKUP;
spin_lock(&parent->d_lock);
new->d_parent = dget_dlock(parent);
hlist_add_head(&new->d_sib, &parent->d_children);
spin_unlock(&parent->d_lock);
retry: retry:
rcu_read_lock(); rcu_read_lock();
seq = smp_load_acquire(&parent->d_inode->i_dir_seq); seq = smp_load_acquire(&parent->d_inode->i_dir_seq);
@ -2620,8 +2642,6 @@ retry:
return dentry; return dentry;
} }
rcu_read_unlock(); rcu_read_unlock();
/* we can't take ->d_lock here; it's OK, though. */
new->d_flags |= DCACHE_PAR_LOOKUP;
new->d_wait = wq; new->d_wait = wq;
hlist_bl_add_head(&new->d_u.d_in_lookup_hash, b); hlist_bl_add_head(&new->d_u.d_in_lookup_hash, b);
hlist_bl_unlock(b); hlist_bl_unlock(b);
@ -2667,7 +2687,8 @@ EXPORT_SYMBOL(__d_lookup_unhash_wake);
/* inode->i_lock held if inode is non-NULL */ /* inode->i_lock held if inode is non-NULL */
static inline void __d_add(struct dentry *dentry, struct inode *inode) static inline void __d_add(struct dentry *dentry, struct inode *inode,
const struct dentry_operations *ops)
{ {
wait_queue_head_t *d_wait; wait_queue_head_t *d_wait;
struct inode *dir = NULL; struct inode *dir = NULL;
@ -2678,6 +2699,8 @@ static inline void __d_add(struct dentry *dentry, struct inode *inode)
n = start_dir_add(dir); n = start_dir_add(dir);
d_wait = __d_lookup_unhash(dentry); d_wait = __d_lookup_unhash(dentry);
} }
if (unlikely(ops))
d_set_d_op(dentry, ops);
if (inode) { if (inode) {
unsigned add_flags = d_flags_for_inode(inode); unsigned add_flags = d_flags_for_inode(inode);
hlist_add_head(&dentry->d_u.d_alias, &inode->i_dentry); hlist_add_head(&dentry->d_u.d_alias, &inode->i_dentry);
@ -2709,7 +2732,7 @@ void d_add(struct dentry *entry, struct inode *inode)
security_d_instantiate(entry, inode); security_d_instantiate(entry, inode);
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
} }
__d_add(entry, inode); __d_add(entry, inode, NULL);
} }
EXPORT_SYMBOL(d_add); EXPORT_SYMBOL(d_add);
@ -2961,30 +2984,8 @@ out_err:
return ret; return ret;
} }
/** struct dentry *d_splice_alias_ops(struct inode *inode, struct dentry *dentry,
* d_splice_alias - splice a disconnected dentry into the tree if one exists const struct dentry_operations *ops)
* @inode: the inode which may have a disconnected dentry
* @dentry: a negative dentry which we want to point to the inode.
*
* If inode is a directory and has an IS_ROOT alias, then d_move that in
* place of the given dentry and return it, else simply d_add the inode
* to the dentry and return NULL.
*
* If a non-IS_ROOT directory is found, the filesystem is corrupt, and
* we should error out: directories can't have multiple aliases.
*
* This is needed in the lookup routine of any filesystem that is exportable
* (via knfsd) so that we can build dcache paths to directories effectively.
*
* If a dentry was found and moved, then it is returned. Otherwise NULL
* is returned. This matches the expected return value of ->lookup.
*
* Cluster filesystems may call this function with a negative, hashed dentry.
* In that case, we know that the inode will be a regular file, and also this
* will only occur during atomic_open. So we need to check for the dentry
* being already hashed only in the final case.
*/
struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry)
{ {
if (IS_ERR(inode)) if (IS_ERR(inode))
return ERR_CAST(inode); return ERR_CAST(inode);
@ -3030,9 +3031,37 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry)
} }
} }
out: out:
__d_add(dentry, inode); __d_add(dentry, inode, ops);
return NULL; return NULL;
} }
/**
* d_splice_alias - splice a disconnected dentry into the tree if one exists
* @inode: the inode which may have a disconnected dentry
* @dentry: a negative dentry which we want to point to the inode.
*
* If inode is a directory and has an IS_ROOT alias, then d_move that in
* place of the given dentry and return it, else simply d_add the inode
* to the dentry and return NULL.
*
* If a non-IS_ROOT directory is found, the filesystem is corrupt, and
* we should error out: directories can't have multiple aliases.
*
* This is needed in the lookup routine of any filesystem that is exportable
* (via knfsd) so that we can build dcache paths to directories effectively.
*
* If a dentry was found and moved, then it is returned. Otherwise NULL
* is returned. This matches the expected return value of ->lookup.
*
* Cluster filesystems may call this function with a negative, hashed dentry.
* In that case, we know that the inode will be a regular file, and also this
* will only occur during atomic_open. So we need to check for the dentry
* being already hashed only in the final case.
*/
struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry)
{
return d_splice_alias_ops(inode, dentry, NULL);
}
EXPORT_SYMBOL(d_splice_alias); EXPORT_SYMBOL(d_splice_alias);
/* /*

View File

@ -258,7 +258,6 @@ static struct vfsmount *debugfs_automount(struct path *path)
} }
static const struct dentry_operations debugfs_dops = { static const struct dentry_operations debugfs_dops = {
.d_delete = always_delete_dentry,
.d_release = debugfs_release_dentry, .d_release = debugfs_release_dentry,
.d_automount = debugfs_automount, .d_automount = debugfs_automount,
}; };
@ -273,7 +272,8 @@ static int debugfs_fill_super(struct super_block *sb, struct fs_context *fc)
return err; return err;
sb->s_op = &debugfs_super_operations; sb->s_op = &debugfs_super_operations;
sb->s_d_op = &debugfs_dops; set_default_d_op(sb, &debugfs_dops);
sb->s_d_flags |= DCACHE_DONTCACHE;
debugfs_apply_options(sb); debugfs_apply_options(sb);

View File

@ -381,7 +381,7 @@ static int devpts_fill_super(struct super_block *s, struct fs_context *fc)
s->s_blocksize_bits = 10; s->s_blocksize_bits = 10;
s->s_magic = DEVPTS_SUPER_MAGIC; s->s_magic = DEVPTS_SUPER_MAGIC;
s->s_op = &devpts_sops; s->s_op = &devpts_sops;
s->s_d_op = &simple_dentry_operations; s->s_d_flags = DCACHE_DONTCACHE;
s->s_time_gran = 1; s->s_time_gran = 1;
fsi->sb = s; fsi->sb = s;

View File

@ -471,7 +471,7 @@ static int ecryptfs_get_tree(struct fs_context *fc)
sbi = NULL; sbi = NULL;
s->s_op = &ecryptfs_sops; s->s_op = &ecryptfs_sops;
s->s_xattr = ecryptfs_xattr_handlers; s->s_xattr = ecryptfs_xattr_handlers;
s->s_d_op = &ecryptfs_dops; set_default_d_op(s, &ecryptfs_dops);
err = "Reading sb failed"; err = "Reading sb failed";
rc = kern_path(fc->source, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path); rc = kern_path(fc->source, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path);

View File

@ -183,7 +183,6 @@ static int efivarfs_d_hash(const struct dentry *dentry, struct qstr *qstr)
static const struct dentry_operations efivarfs_d_ops = { static const struct dentry_operations efivarfs_d_ops = {
.d_compare = efivarfs_d_compare, .d_compare = efivarfs_d_compare,
.d_hash = efivarfs_d_hash, .d_hash = efivarfs_d_hash,
.d_delete = always_delete_dentry,
}; };
static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name) static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name)
@ -350,7 +349,8 @@ static int efivarfs_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_blocksize_bits = PAGE_SHIFT; sb->s_blocksize_bits = PAGE_SHIFT;
sb->s_magic = EFIVARFS_MAGIC; sb->s_magic = EFIVARFS_MAGIC;
sb->s_op = &efivarfs_ops; sb->s_op = &efivarfs_ops;
sb->s_d_op = &efivarfs_d_ops; set_default_d_op(sb, &efivarfs_d_ops);
sb->s_d_flags |= DCACHE_DONTCACHE;
sb->s_time_gran = 1; sb->s_time_gran = 1;
if (!efivar_supports_writes()) if (!efivar_supports_writes())

View File

@ -667,9 +667,9 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc)
} }
if (sbi->options.utf8) if (sbi->options.utf8)
sb->s_d_op = &exfat_utf8_dentry_ops; set_default_d_op(sb, &exfat_utf8_dentry_ops);
else else
sb->s_d_op = &exfat_dentry_ops; set_default_d_op(sb, &exfat_dentry_ops);
root_inode = new_inode(sb); root_inode = new_inode(sb);
if (!root_inode) { if (!root_inode) {

View File

@ -646,7 +646,7 @@ static const struct inode_operations msdos_dir_inode_operations = {
static void setup(struct super_block *sb) static void setup(struct super_block *sb)
{ {
MSDOS_SB(sb)->dir_ops = &msdos_dir_inode_operations; MSDOS_SB(sb)->dir_ops = &msdos_dir_inode_operations;
sb->s_d_op = &msdos_dentry_operations; set_default_d_op(sb, &msdos_dentry_operations);
sb->s_flags |= SB_NOATIME; sb->s_flags |= SB_NOATIME;
} }

View File

@ -1187,9 +1187,9 @@ static void setup(struct super_block *sb)
{ {
MSDOS_SB(sb)->dir_ops = &vfat_dir_inode_operations; MSDOS_SB(sb)->dir_ops = &vfat_dir_inode_operations;
if (MSDOS_SB(sb)->options.name_check != 's') if (MSDOS_SB(sb)->options.name_check != 's')
sb->s_d_op = &vfat_ci_dentry_ops; set_default_d_op(sb, &vfat_ci_dentry_ops);
else else
sb->s_d_op = &vfat_dentry_ops; set_default_d_op(sb, &vfat_dentry_ops);
} }
static int vfat_fill_super(struct super_block *sb, struct fs_context *fc) static int vfat_fill_super(struct super_block *sb, struct fs_context *fc)

View File

@ -338,13 +338,6 @@ const struct dentry_operations fuse_dentry_operations = {
.d_automount = fuse_dentry_automount, .d_automount = fuse_dentry_automount,
}; };
const struct dentry_operations fuse_root_dentry_operations = {
#if BITS_PER_LONG < 64
.d_init = fuse_dentry_init,
.d_release = fuse_dentry_release,
#endif
};
int fuse_valid_type(int m) int fuse_valid_type(int m)
{ {
return S_ISREG(m) || S_ISDIR(m) || S_ISLNK(m) || S_ISCHR(m) || return S_ISREG(m) || S_ISDIR(m) || S_ISLNK(m) || S_ISCHR(m) ||

View File

@ -1109,7 +1109,6 @@ static inline void fuse_sync_bucket_dec(struct fuse_sync_bucket *bucket)
extern const struct file_operations fuse_dev_operations; extern const struct file_operations fuse_dev_operations;
extern const struct dentry_operations fuse_dentry_operations; extern const struct dentry_operations fuse_dentry_operations;
extern const struct dentry_operations fuse_root_dentry_operations;
/** /**
* Get a filled in inode * Get a filled in inode

View File

@ -1719,7 +1719,7 @@ static int fuse_fill_super_submount(struct super_block *sb,
fi = get_fuse_inode(root); fi = get_fuse_inode(root);
fi->nlookup--; fi->nlookup--;
sb->s_d_op = &fuse_dentry_operations; set_default_d_op(sb, &fuse_dentry_operations);
sb->s_root = d_make_root(root); sb->s_root = d_make_root(root);
if (!sb->s_root) if (!sb->s_root)
return -ENOMEM; return -ENOMEM;
@ -1854,12 +1854,10 @@ int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx)
err = -ENOMEM; err = -ENOMEM;
root = fuse_get_root_inode(sb, ctx->rootmode); root = fuse_get_root_inode(sb, ctx->rootmode);
sb->s_d_op = &fuse_root_dentry_operations; set_default_d_op(sb, &fuse_dentry_operations);
root_dentry = d_make_root(root); root_dentry = d_make_root(root);
if (!root_dentry) if (!root_dentry)
goto err_dev_free; goto err_dev_free;
/* Root dentry doesn't have .d_revalidate */
sb->s_d_op = &fuse_dentry_operations;
mutex_lock(&fuse_mutex); mutex_lock(&fuse_mutex);
err = -EINVAL; err = -EINVAL;

View File

@ -1145,7 +1145,7 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_magic = GFS2_MAGIC; sb->s_magic = GFS2_MAGIC;
sb->s_op = &gfs2_super_ops; sb->s_op = &gfs2_super_ops;
sb->s_d_op = &gfs2_dops; set_default_d_op(sb, &gfs2_dops);
sb->s_export_op = &gfs2_export_ops; sb->s_export_op = &gfs2_export_ops;
sb->s_qcop = &gfs2_quotactl_ops; sb->s_qcop = &gfs2_quotactl_ops;
sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP; sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP;

View File

@ -365,7 +365,7 @@ static int hfs_fill_super(struct super_block *sb, struct fs_context *fc)
if (!root_inode) if (!root_inode)
goto bail_no_root; goto bail_no_root;
sb->s_d_op = &hfs_dentry_operations; set_default_d_op(sb, &hfs_dentry_operations);
res = -ENOMEM; res = -ENOMEM;
sb->s_root = d_make_root(root_inode); sb->s_root = d_make_root(root_inode);
if (!sb->s_root) if (!sb->s_root)

View File

@ -508,7 +508,7 @@ static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc)
goto out_put_alloc_file; goto out_put_alloc_file;
} }
sb->s_d_op = &hfsplus_dentry_operations; set_default_d_op(sb, &hfsplus_dentry_operations);
sb->s_root = d_make_root(root); sb->s_root = d_make_root(root);
if (!sb->s_root) { if (!sb->s_root) {
err = -ENOMEM; err = -ENOMEM;

View File

@ -933,7 +933,7 @@ static int hostfs_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_blocksize_bits = 10; sb->s_blocksize_bits = 10;
sb->s_magic = HOSTFS_SUPER_MAGIC; sb->s_magic = HOSTFS_SUPER_MAGIC;
sb->s_op = &hostfs_sbops; sb->s_op = &hostfs_sbops;
sb->s_d_op = &simple_dentry_operations; sb->s_d_flags = DCACHE_DONTCACHE;
sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_maxbytes = MAX_LFS_FILESIZE;
err = super_setup_bdi(sb); err = super_setup_bdi(sb);
if (err) if (err)

View File

@ -554,7 +554,7 @@ static int hpfs_fill_super(struct super_block *s, struct fs_context *fc)
/* Fill superblock stuff */ /* Fill superblock stuff */
s->s_magic = HPFS_SUPER_MAGIC; s->s_magic = HPFS_SUPER_MAGIC;
s->s_op = &hpfs_sops; s->s_op = &hpfs_sops;
s->s_d_op = &hpfs_dentry_operations; set_default_d_op(s, &hpfs_dentry_operations);
s->s_time_min = local_to_gmt(s, 0); s->s_time_min = local_to_gmt(s, 0);
s->s_time_max = local_to_gmt(s, U32_MAX); s->s_time_max = local_to_gmt(s, U32_MAX);

View File

@ -1433,6 +1433,7 @@ hugetlbfs_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_blocksize_bits = huge_page_shift(ctx->hstate); sb->s_blocksize_bits = huge_page_shift(ctx->hstate);
sb->s_magic = HUGETLBFS_MAGIC; sb->s_magic = HUGETLBFS_MAGIC;
sb->s_op = &hugetlbfs_ops; sb->s_op = &hugetlbfs_ops;
sb->s_d_flags = DCACHE_DONTCACHE;
sb->s_time_gran = 1; sb->s_time_gran = 1;
/* /*

View File

@ -939,7 +939,7 @@ root_found:
sbi->s_check = opt->check; sbi->s_check = opt->check;
if (table) if (table)
s->s_d_op = &isofs_dentry_ops[table - 1]; set_default_d_op(s, &isofs_dentry_ops[table - 1]);
/* get the root dentry */ /* get the root dentry */
s->s_root = d_make_root(inode); s->s_root = d_make_root(inode);

View File

@ -542,7 +542,7 @@ static int jfs_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_magic = JFS_SUPER_MAGIC; sb->s_magic = JFS_SUPER_MAGIC;
if (sbi->mntflag & JFS_OS2) if (sbi->mntflag & JFS_OS2)
sb->s_d_op = &jfs_ci_dentry_operations; set_default_d_op(sb, &jfs_ci_dentry_operations);
inode = jfs_iget(sb, ROOT_I); inode = jfs_iget(sb, ROOT_I);
if (IS_ERR(inode)) { if (IS_ERR(inode)) {

View File

@ -318,7 +318,7 @@ static int kernfs_fill_super(struct super_block *sb, struct kernfs_fs_context *k
return -ENOMEM; return -ENOMEM;
} }
sb->s_root = root; sb->s_root = root;
sb->s_d_op = &kernfs_dops; set_default_d_op(sb, &kernfs_dops);
return 0; return 0;
} }

View File

@ -62,11 +62,6 @@ int always_delete_dentry(const struct dentry *dentry)
} }
EXPORT_SYMBOL(always_delete_dentry); EXPORT_SYMBOL(always_delete_dentry);
const struct dentry_operations simple_dentry_operations = {
.d_delete = always_delete_dentry,
};
EXPORT_SYMBOL(simple_dentry_operations);
/* /*
* Lookup the data. This is trivial - if the dentry didn't already * Lookup the data. This is trivial - if the dentry didn't already
* exist, we know it is negative. Set d_op to delete negative dentries. * exist, we know it is negative. Set d_op to delete negative dentries.
@ -75,9 +70,11 @@ struct dentry *simple_lookup(struct inode *dir, struct dentry *dentry, unsigned
{ {
if (dentry->d_name.len > NAME_MAX) if (dentry->d_name.len > NAME_MAX)
return ERR_PTR(-ENAMETOOLONG); return ERR_PTR(-ENAMETOOLONG);
if (!dentry->d_sb->s_d_op) if (!dentry->d_op && !(dentry->d_flags & DCACHE_DONTCACHE)) {
d_set_d_op(dentry, &simple_dentry_operations); spin_lock(&dentry->d_lock);
dentry->d_flags |= DCACHE_DONTCACHE;
spin_unlock(&dentry->d_lock);
}
if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir)) if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir))
return NULL; return NULL;
@ -684,7 +681,7 @@ static int pseudo_fs_fill_super(struct super_block *s, struct fs_context *fc)
s->s_root = d_make_root(root); s->s_root = d_make_root(root);
if (!s->s_root) if (!s->s_root)
return -ENOMEM; return -ENOMEM;
s->s_d_op = ctx->dops; set_default_d_op(s, ctx->dops);
return 0; return 0;
} }
@ -1948,22 +1945,22 @@ static const struct dentry_operations generic_encrypted_dentry_ops = {
* @sb: superblock to be configured * @sb: superblock to be configured
* *
* Filesystems supporting casefolding and/or fscrypt can call this * Filesystems supporting casefolding and/or fscrypt can call this
* helper at mount-time to configure sb->s_d_op to best set of dentry * helper at mount-time to configure default dentry_operations to the
* operations required for the enabled features. The helper must be * best set of dentry operations required for the enabled features.
* called after these have been configured, but before the root dentry * The helper must be called after these have been configured, but
* is created. * before the root dentry is created.
*/ */
void generic_set_sb_d_ops(struct super_block *sb) void generic_set_sb_d_ops(struct super_block *sb)
{ {
#if IS_ENABLED(CONFIG_UNICODE) #if IS_ENABLED(CONFIG_UNICODE)
if (sb->s_encoding) { if (sb->s_encoding) {
sb->s_d_op = &generic_ci_dentry_ops; set_default_d_op(sb, &generic_ci_dentry_ops);
return; return;
} }
#endif #endif
#ifdef CONFIG_FS_ENCRYPTION #ifdef CONFIG_FS_ENCRYPTION
if (sb->s_cop) { if (sb->s_cop) {
sb->s_d_op = &generic_encrypted_dentry_ops; set_default_d_op(sb, &generic_encrypted_dentry_ops);
return; return;
} }
#endif #endif

View File

@ -1183,7 +1183,7 @@ static int nfs_set_super(struct super_block *s, struct fs_context *fc)
struct nfs_server *server = fc->s_fs_info; struct nfs_server *server = fc->s_fs_info;
int ret; int ret;
s->s_d_op = server->nfs_client->rpc_ops->dentry_ops; set_default_d_op(s, server->nfs_client->rpc_ops->dentry_ops);
ret = set_anon_super(s, server); ret = set_anon_super(s, server);
if (ret == 0) if (ret == 0)
server->s_dev = s->s_dev; server->s_dev = s->s_dev;

View File

@ -1223,7 +1223,8 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_export_op = &ntfs_export_ops; sb->s_export_op = &ntfs_export_ops;
sb->s_time_gran = NTFS_TIME_GRAN; // 100 nsec sb->s_time_gran = NTFS_TIME_GRAN; // 100 nsec
sb->s_xattr = ntfs_xattr_handlers; sb->s_xattr = ntfs_xattr_handlers;
sb->s_d_op = options->nocase ? &ntfs_dentry_ops : NULL; if (options->nocase)
set_default_d_op(sb, &ntfs_dentry_ops);
options->nls = ntfs_load_nls(options->nls_name); options->nls = ntfs_load_nls(options->nls_name);
if (IS_ERR(options->nls)) { if (IS_ERR(options->nls)) {

View File

@ -1962,7 +1962,7 @@ static int ocfs2_initialize_super(struct super_block *sb,
sb->s_fs_info = osb; sb->s_fs_info = osb;
sb->s_op = &ocfs2_sops; sb->s_op = &ocfs2_sops;
sb->s_d_op = &ocfs2_dentry_ops; set_default_d_op(sb, &ocfs2_dentry_ops);
sb->s_export_op = &ocfs2_export_ops; sb->s_export_op = &ocfs2_export_ops;
sb->s_qcop = &dquot_quotactl_sysfile_ops; sb->s_qcop = &dquot_quotactl_sysfile_ops;
sb->dq_op = &ocfs2_quota_operations; sb->dq_op = &ocfs2_quota_operations;

View File

@ -416,7 +416,7 @@ static int orangefs_fill_sb(struct super_block *sb,
sb->s_xattr = orangefs_xattr_handlers; sb->s_xattr = orangefs_xattr_handlers;
sb->s_magic = ORANGEFS_SUPER_MAGIC; sb->s_magic = ORANGEFS_SUPER_MAGIC;
sb->s_op = &orangefs_s_ops; sb->s_op = &orangefs_s_ops;
sb->s_d_op = &orangefs_dentry_operations; set_default_d_op(sb, &orangefs_dentry_operations);
sb->s_blocksize = PAGE_SIZE; sb->s_blocksize = PAGE_SIZE;
sb->s_blocksize_bits = PAGE_SHIFT; sb->s_blocksize_bits = PAGE_SHIFT;

View File

@ -1322,7 +1322,7 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc)
if (WARN_ON(fc->user_ns != current_user_ns())) if (WARN_ON(fc->user_ns != current_user_ns()))
goto out_err; goto out_err;
sb->s_d_op = &ovl_dentry_operations; set_default_d_op(sb, &ovl_dentry_operations);
err = -ENOMEM; err = -ENOMEM;
if (!ofs->creator_cred) if (!ofs->creator_cred)

View File

@ -2704,8 +2704,7 @@ static struct dentry *proc_pident_instantiate(struct dentry *dentry,
inode->i_fop = p->fop; inode->i_fop = p->fop;
ei->op = p->op; ei->op = p->op;
pid_update_inode(task, inode); pid_update_inode(task, inode);
d_set_d_op(dentry, &pid_dentry_operations); return d_splice_alias_ops(inode, dentry, &pid_dentry_operations);
return d_splice_alias(inode, dentry);
} }
static struct dentry *proc_pident_lookup(struct inode *dir, static struct dentry *proc_pident_lookup(struct inode *dir,
@ -3501,8 +3500,7 @@ static struct dentry *proc_pid_instantiate(struct dentry * dentry,
set_nlink(inode, nlink_tgid); set_nlink(inode, nlink_tgid);
pid_update_inode(task, inode); pid_update_inode(task, inode);
d_set_d_op(dentry, &pid_dentry_operations); return d_splice_alias_ops(inode, dentry, &pid_dentry_operations);
return d_splice_alias(inode, dentry);
} }
struct dentry *proc_pid_lookup(struct dentry *dentry, unsigned int flags) struct dentry *proc_pid_lookup(struct dentry *dentry, unsigned int flags)
@ -3804,8 +3802,7 @@ static struct dentry *proc_task_instantiate(struct dentry *dentry,
set_nlink(inode, nlink_tid); set_nlink(inode, nlink_tid);
pid_update_inode(task, inode); pid_update_inode(task, inode);
d_set_d_op(dentry, &pid_dentry_operations); return d_splice_alias_ops(inode, dentry, &pid_dentry_operations);
return d_splice_alias(inode, dentry);
} }
static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry, unsigned int flags) static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry, unsigned int flags)

View File

@ -254,8 +254,11 @@ struct dentry *proc_lookup_de(struct inode *dir, struct dentry *dentry,
inode = proc_get_inode(dir->i_sb, de); inode = proc_get_inode(dir->i_sb, de);
if (!inode) if (!inode)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
d_set_d_op(dentry, de->proc_dops); if (de->flags & PROC_ENTRY_FORCE_LOOKUP)
return d_splice_alias(inode, dentry); return d_splice_alias_ops(inode, dentry,
&proc_net_dentry_ops);
return d_splice_alias_ops(inode, dentry,
&proc_misc_dentry_ops);
} }
read_unlock(&proc_subdir_lock); read_unlock(&proc_subdir_lock);
return ERR_PTR(-ENOENT); return ERR_PTR(-ENOENT);
@ -448,9 +451,8 @@ static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent,
INIT_LIST_HEAD(&ent->pde_openers); INIT_LIST_HEAD(&ent->pde_openers);
proc_set_user(ent, (*parent)->uid, (*parent)->gid); proc_set_user(ent, (*parent)->uid, (*parent)->gid);
ent->proc_dops = &proc_misc_dentry_ops;
/* Revalidate everything under /proc/${pid}/net */ /* Revalidate everything under /proc/${pid}/net */
if ((*parent)->proc_dops == &proc_net_dentry_ops) if ((*parent)->flags & PROC_ENTRY_FORCE_LOOKUP)
pde_force_lookup(ent); pde_force_lookup(ent);
out: out:

View File

@ -44,7 +44,6 @@ struct proc_dir_entry {
const struct proc_ops *proc_ops; const struct proc_ops *proc_ops;
const struct file_operations *proc_dir_ops; const struct file_operations *proc_dir_ops;
}; };
const struct dentry_operations *proc_dops;
union { union {
const struct seq_operations *seq_ops; const struct seq_operations *seq_ops;
int (*single_show)(struct seq_file *, void *); int (*single_show)(struct seq_file *, void *);
@ -403,7 +402,7 @@ extern const struct dentry_operations proc_net_dentry_ops;
static inline void pde_force_lookup(struct proc_dir_entry *pde) static inline void pde_force_lookup(struct proc_dir_entry *pde)
{ {
/* /proc/net/ entries can be changed under us by setns(CLONE_NEWNET) */ /* /proc/net/ entries can be changed under us by setns(CLONE_NEWNET) */
pde->proc_dops = &proc_net_dentry_ops; pde->flags |= PROC_ENTRY_FORCE_LOOKUP;
} }
/* /*
@ -414,7 +413,6 @@ static inline void pde_force_lookup(struct proc_dir_entry *pde)
static inline struct dentry *proc_splice_unmountable(struct inode *inode, static inline struct dentry *proc_splice_unmountable(struct inode *inode,
struct dentry *dentry, const struct dentry_operations *d_ops) struct dentry *dentry, const struct dentry_operations *d_ops)
{ {
d_set_d_op(dentry, d_ops);
dont_mount(dentry); dont_mount(dentry);
return d_splice_alias(inode, dentry); return d_splice_alias_ops(inode, dentry, d_ops);
} }

View File

@ -111,8 +111,7 @@ static struct dentry *proc_ns_instantiate(struct dentry *dentry,
ei->ns_ops = ns_ops; ei->ns_ops = ns_ops;
pid_update_inode(task, inode); pid_update_inode(task, inode);
d_set_d_op(dentry, &pid_dentry_operations); return d_splice_alias_ops(inode, dentry, &pid_dentry_operations);
return d_splice_alias(inode, dentry);
} }
static int proc_ns_dir_readdir(struct file *file, struct dir_context *ctx) static int proc_ns_dir_readdir(struct file *file, struct dir_context *ctx)

View File

@ -540,9 +540,8 @@ static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,
goto out; goto out;
} }
d_set_d_op(dentry, &proc_sys_dentry_operations);
inode = proc_sys_make_inode(dir->i_sb, h ? h : head, p); inode = proc_sys_make_inode(dir->i_sb, h ? h : head, p);
err = d_splice_alias(inode, dentry); err = d_splice_alias_ops(inode, dentry, &proc_sys_dentry_operations);
out: out:
if (h) if (h)
@ -699,9 +698,9 @@ static bool proc_sys_fill_cache(struct file *file,
return false; return false;
if (d_in_lookup(child)) { if (d_in_lookup(child)) {
struct dentry *res; struct dentry *res;
d_set_d_op(child, &proc_sys_dentry_operations);
inode = proc_sys_make_inode(dir->d_sb, head, table); inode = proc_sys_make_inode(dir->d_sb, head, table);
res = d_splice_alias(inode, child); res = d_splice_alias_ops(inode, child,
&proc_sys_dentry_operations);
d_lookup_done(child); d_lookup_done(child);
if (unlikely(res)) { if (unlikely(res)) {
dput(child); dput(child);

View File

@ -269,6 +269,7 @@ static int ramfs_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_blocksize_bits = PAGE_SHIFT; sb->s_blocksize_bits = PAGE_SHIFT;
sb->s_magic = RAMFS_MAGIC; sb->s_magic = RAMFS_MAGIC;
sb->s_op = &ramfs_ops; sb->s_op = &ramfs_ops;
sb->s_d_flags = DCACHE_DONTCACHE;
sb->s_time_gran = 1; sb->s_time_gran = 1;
inode = ramfs_get_inode(sb, NULL, S_IFDIR | fsi->mount_opts.mode, 0); inode = ramfs_get_inode(sb, NULL, S_IFDIR | fsi->mount_opts.mode, 0);

View File

@ -260,9 +260,9 @@ cifs_read_super(struct super_block *sb)
} }
if (tcon->nocase) if (tcon->nocase)
sb->s_d_op = &cifs_ci_dentry_ops; set_default_d_op(sb, &cifs_ci_dentry_ops);
else else
sb->s_d_op = &cifs_dentry_ops; set_default_d_op(sb, &cifs_dentry_ops);
sb->s_root = d_make_root(inode); sb->s_root = d_make_root(inode);
if (!sb->s_root) { if (!sb->s_root) {

View File

@ -465,9 +465,20 @@ static int tracefs_d_revalidate(struct inode *inode, const struct qstr *name,
return !(ei && ei->is_freed); return !(ei && ei->is_freed);
} }
static int tracefs_d_delete(const struct dentry *dentry)
{
/*
* We want to keep eventfs dentries around but not tracefs
* ones. eventfs dentries have content in d_fsdata.
* Use d_fsdata to determine if it's a eventfs dentry or not.
*/
return dentry->d_fsdata == NULL;
}
static const struct dentry_operations tracefs_dentry_operations = { static const struct dentry_operations tracefs_dentry_operations = {
.d_revalidate = tracefs_d_revalidate, .d_revalidate = tracefs_d_revalidate,
.d_release = tracefs_d_release, .d_release = tracefs_d_release,
.d_delete = tracefs_d_delete,
}; };
static int tracefs_fill_super(struct super_block *sb, struct fs_context *fc) static int tracefs_fill_super(struct super_block *sb, struct fs_context *fc)
@ -480,7 +491,7 @@ static int tracefs_fill_super(struct super_block *sb, struct fs_context *fc)
return err; return err;
sb->s_op = &tracefs_super_operations; sb->s_op = &tracefs_super_operations;
sb->s_d_op = &tracefs_dentry_operations; set_default_d_op(sb, &tracefs_dentry_operations);
return 0; return 0;
} }

View File

@ -189,7 +189,7 @@ static int vboxsf_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_blocksize = 1024; sb->s_blocksize = 1024;
sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_op = &vboxsf_super_ops; sb->s_op = &vboxsf_super_ops;
sb->s_d_op = &vboxsf_dentry_ops; set_default_d_op(sb, &vboxsf_dentry_ops);
iroot = iget_locked(sb, 0); iroot = iget_locked(sb, 0);
if (!iroot) { if (!iroot) {

View File

@ -237,7 +237,6 @@ extern void d_instantiate_new(struct dentry *, struct inode *);
extern void __d_drop(struct dentry *dentry); extern void __d_drop(struct dentry *dentry);
extern void d_drop(struct dentry *dentry); extern void d_drop(struct dentry *dentry);
extern void d_delete(struct dentry *); extern void d_delete(struct dentry *);
extern void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op);
/* allocate/de-allocate */ /* allocate/de-allocate */
extern struct dentry * d_alloc(struct dentry *, const struct qstr *); extern struct dentry * d_alloc(struct dentry *, const struct qstr *);
@ -245,6 +244,9 @@ extern struct dentry * d_alloc_anon(struct super_block *);
extern struct dentry * d_alloc_parallel(struct dentry *, const struct qstr *, extern struct dentry * d_alloc_parallel(struct dentry *, const struct qstr *,
wait_queue_head_t *); wait_queue_head_t *);
extern struct dentry * d_splice_alias(struct inode *, struct dentry *); extern struct dentry * d_splice_alias(struct inode *, struct dentry *);
/* weird procfs mess; *NOT* exported */
extern struct dentry * d_splice_alias_ops(struct inode *, struct dentry *,
const struct dentry_operations *);
extern struct dentry * d_add_ci(struct dentry *, struct inode *, struct qstr *); extern struct dentry * d_add_ci(struct dentry *, struct inode *, struct qstr *);
extern bool d_same_name(const struct dentry *dentry, const struct dentry *parent, extern bool d_same_name(const struct dentry *dentry, const struct dentry *parent,
const struct qstr *name); const struct qstr *name);
@ -604,4 +606,6 @@ static inline struct dentry *d_next_sibling(const struct dentry *dentry)
return hlist_entry_safe(dentry->d_sib.next, struct dentry, d_sib); return hlist_entry_safe(dentry->d_sib.next, struct dentry, d_sib);
} }
void set_default_d_op(struct super_block *, const struct dentry_operations *);
#endif /* __LINUX_DCACHE_H */ #endif /* __LINUX_DCACHE_H */

View File

@ -1402,6 +1402,7 @@ struct super_block {
char s_sysfs_name[UUID_STRING_LEN + 1]; char s_sysfs_name[UUID_STRING_LEN + 1];
unsigned int s_max_links; unsigned int s_max_links;
unsigned int s_d_flags; /* default d_flags for dentries */
/* /*
* The next field is for VFS *only*. No filesystems have any business * The next field is for VFS *only*. No filesystems have any business
@ -1415,7 +1416,7 @@ struct super_block {
*/ */
const char *s_subtype; const char *s_subtype;
const struct dentry_operations *s_d_op; /* default d_op for dentries */ const struct dentry_operations *__s_d_op; /* default d_op for dentries */
struct shrinker *s_shrink; /* per-sb shrinker handle */ struct shrinker *s_shrink; /* per-sb shrinker handle */
@ -3611,7 +3612,6 @@ extern struct inode *alloc_anon_inode(struct super_block *);
struct inode *anon_inode_make_secure_inode(struct super_block *sb, const char *name, struct inode *anon_inode_make_secure_inode(struct super_block *sb, const char *name,
const struct inode *context_inode); const struct inode *context_inode);
extern int simple_nosetlease(struct file *, int, struct file_lease **, void **); extern int simple_nosetlease(struct file *, int, struct file_lease **, void **);
extern const struct dentry_operations simple_dentry_operations;
extern struct dentry *simple_lookup(struct inode *, struct dentry *, unsigned int flags); extern struct dentry *simple_lookup(struct inode *, struct dentry *, unsigned int flags);
extern ssize_t generic_read_dir(struct file *, char __user *, size_t, loff_t *); extern ssize_t generic_read_dir(struct file *, char __user *, size_t, loff_t *);

View File

@ -27,6 +27,8 @@ enum {
PROC_ENTRY_proc_read_iter = 1U << 1, PROC_ENTRY_proc_read_iter = 1U << 1,
PROC_ENTRY_proc_compat_ioctl = 1U << 2, PROC_ENTRY_proc_compat_ioctl = 1U << 2,
PROC_ENTRY_FORCE_LOOKUP = 1U << 7,
}; };
struct proc_ops { struct proc_ops {

View File

@ -411,6 +411,7 @@ static int mqueue_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_blocksize_bits = PAGE_SHIFT; sb->s_blocksize_bits = PAGE_SHIFT;
sb->s_magic = MQUEUE_MAGIC; sb->s_magic = MQUEUE_MAGIC;
sb->s_op = &mqueue_super_ops; sb->s_op = &mqueue_super_ops;
sb->s_d_flags = DCACHE_DONTCACHE;
inode = mqueue_get_inode(sb, ns, S_IFDIR | S_ISVTX | S_IRWXUGO, NULL); inode = mqueue_get_inode(sb, ns, S_IFDIR | S_ISVTX | S_IRWXUGO, NULL);
if (IS_ERR(inode)) if (IS_ERR(inode))

View File

@ -4984,7 +4984,6 @@ static void shmem_put_super(struct super_block *sb)
static const struct dentry_operations shmem_ci_dentry_ops = { static const struct dentry_operations shmem_ci_dentry_ops = {
.d_hash = generic_ci_d_hash, .d_hash = generic_ci_d_hash,
.d_compare = generic_ci_d_compare, .d_compare = generic_ci_d_compare,
.d_delete = always_delete_dentry,
}; };
#endif #endif
@ -5032,7 +5031,7 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc)
if (ctx->encoding) { if (ctx->encoding) {
sb->s_encoding = ctx->encoding; sb->s_encoding = ctx->encoding;
sb->s_d_op = &shmem_ci_dentry_ops; set_default_d_op(sb, &shmem_ci_dentry_ops);
if (ctx->strict_encoding) if (ctx->strict_encoding)
sb->s_encoding_flags = SB_ENC_STRICT_MODE_FL; sb->s_encoding_flags = SB_ENC_STRICT_MODE_FL;
} }
@ -5041,6 +5040,7 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc)
#else #else
sb->s_flags |= SB_NOUSER; sb->s_flags |= SB_NOUSER;
#endif /* CONFIG_TMPFS */ #endif /* CONFIG_TMPFS */
sb->s_d_flags |= DCACHE_DONTCACHE;
sbinfo->max_blocks = ctx->blocks; sbinfo->max_blocks = ctx->blocks;
sbinfo->max_inodes = ctx->inodes; sbinfo->max_inodes = ctx->inodes;
sbinfo->free_ispace = sbinfo->max_inodes * BOGO_INODE_SIZE; sbinfo->free_ispace = sbinfo->max_inodes * BOGO_INODE_SIZE;

View File

@ -1363,7 +1363,7 @@ rpc_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_blocksize_bits = PAGE_SHIFT; sb->s_blocksize_bits = PAGE_SHIFT;
sb->s_magic = RPCAUTH_GSSMAGIC; sb->s_magic = RPCAUTH_GSSMAGIC;
sb->s_op = &s_ops; sb->s_op = &s_ops;
sb->s_d_op = &simple_dentry_operations; sb->s_d_flags = DCACHE_DONTCACHE;
sb->s_time_gran = 1; sb->s_time_gran = 1;
inode = rpc_get_inode(sb, S_IFDIR | 0555); inode = rpc_get_inode(sb, S_IFDIR | 0555);