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:
commit
11fe69fbd5
@ -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
|
||||
created. These struct path point to locations in the caller's namespace
|
||||
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.
|
||||
|
@ -127,7 +127,6 @@ const struct dentry_operations v9fs_cached_dentry_operations = {
|
||||
};
|
||||
|
||||
const struct dentry_operations v9fs_dentry_operations = {
|
||||
.d_delete = always_delete_dentry,
|
||||
.d_release = v9fs_dentry_release,
|
||||
.d_unalias_trylock = v9fs_dentry_unalias_trylock,
|
||||
.d_unalias_unlock = v9fs_dentry_unalias_unlock,
|
||||
|
@ -134,10 +134,12 @@ static struct dentry *v9fs_mount(struct file_system_type *fs_type, int flags,
|
||||
if (retval)
|
||||
goto release_sb;
|
||||
|
||||
if (v9ses->cache & (CACHE_META|CACHE_LOOSE))
|
||||
sb->s_d_op = &v9fs_cached_dentry_operations;
|
||||
else
|
||||
sb->s_d_op = &v9fs_dentry_operations;
|
||||
if (v9ses->cache & (CACHE_META|CACHE_LOOSE)) {
|
||||
set_default_d_op(sb, &v9fs_cached_dentry_operations);
|
||||
} else {
|
||||
set_default_d_op(sb, &v9fs_dentry_operations);
|
||||
sb->s_d_flags |= DCACHE_DONTCACHE;
|
||||
}
|
||||
|
||||
inode = v9fs_get_new_inode_from_fid(v9ses, fid, sb);
|
||||
if (IS_ERR(inode)) {
|
||||
|
@ -397,7 +397,7 @@ static int adfs_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
if (asb->s_ftsuffix)
|
||||
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);
|
||||
sb->s_root = d_make_root(root);
|
||||
if (!sb->s_root) {
|
||||
|
@ -500,9 +500,9 @@ got_root:
|
||||
return PTR_ERR(root_inode);
|
||||
|
||||
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
|
||||
sb->s_d_op = &affs_dentry_operations;
|
||||
set_default_d_op(sb, &affs_dentry_operations);
|
||||
|
||||
sb->s_root = d_make_root(root_inode);
|
||||
if (!sb->s_root) {
|
||||
|
@ -483,9 +483,9 @@ static int afs_fill_super(struct super_block *sb, struct afs_fs_context *ctx)
|
||||
goto error;
|
||||
|
||||
if (as->dyn_root) {
|
||||
sb->s_d_op = &afs_dynroot_dentry_operations;
|
||||
set_default_d_op(sb, &afs_dynroot_dentry_operations);
|
||||
} 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);
|
||||
}
|
||||
|
||||
|
@ -311,7 +311,7 @@ static int autofs_fill_super(struct super_block *s, struct fs_context *fc)
|
||||
s->s_blocksize_bits = 10;
|
||||
s->s_magic = AUTOFS_SUPER_MAGIC;
|
||||
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;
|
||||
|
||||
/*
|
||||
|
@ -959,7 +959,7 @@ static int btrfs_fill_super(struct super_block *sb,
|
||||
sb->s_maxbytes = MAX_LFS_FILESIZE;
|
||||
sb->s_magic = BTRFS_SUPER_MAGIC;
|
||||
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;
|
||||
#ifdef CONFIG_FS_VERITY
|
||||
sb->s_vop = &btrfs_verityops;
|
||||
|
@ -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 */
|
||||
|
||||
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_time_gran = 1;
|
||||
|
@ -230,7 +230,7 @@ static int coda_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
sb->s_blocksize_bits = 12;
|
||||
sb->s_magic = CODA_SUPER_MAGIC;
|
||||
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_min = S64_MIN;
|
||||
sb->s_time_max = S64_MAX;
|
||||
|
@ -67,7 +67,6 @@ static void configfs_d_iput(struct dentry * dentry,
|
||||
|
||||
const struct dentry_operations configfs_dentry_ops = {
|
||||
.d_iput = configfs_d_iput,
|
||||
.d_delete = always_delete_dentry,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_LOCKDEP
|
||||
|
@ -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;
|
||||
root->d_fsdata = &configfs_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;
|
||||
}
|
||||
|
||||
|
155
fs/dcache.c
155
fs/dcache.c
@ -1436,7 +1436,7 @@ int d_set_mounted(struct dentry *dentry)
|
||||
{
|
||||
struct dentry *p;
|
||||
int ret = -ENOENT;
|
||||
write_seqlock(&rename_lock);
|
||||
read_seqlock_excl(&rename_lock);
|
||||
for (p = dentry->d_parent; !IS_ROOT(p); p = p->d_parent) {
|
||||
/* Need exclusion wrt. d_invalidate() */
|
||||
spin_lock(&p->d_lock);
|
||||
@ -1456,7 +1456,7 @@ int d_set_mounted(struct dentry *dentry)
|
||||
}
|
||||
spin_unlock(&dentry->d_lock);
|
||||
out:
|
||||
write_sequnlock(&rename_lock);
|
||||
read_sequnlock_excl(&rename_lock);
|
||||
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_parent = dentry;
|
||||
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;
|
||||
INIT_HLIST_BL_NODE(&dentry->d_hash);
|
||||
INIT_LIST_HEAD(&dentry->d_lru);
|
||||
INIT_HLIST_HEAD(&dentry->d_children);
|
||||
INIT_HLIST_NODE(&dentry->d_u.d_alias);
|
||||
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) {
|
||||
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);
|
||||
if (likely(dentry)) {
|
||||
dentry->d_flags |= DCACHE_NORCU;
|
||||
if (!sb->s_d_op)
|
||||
d_set_d_op(dentry, &anon_ops);
|
||||
/* d_op_flags(&anon_ops) is 0 */
|
||||
if (!dentry->d_op)
|
||||
dentry->d_op = &anon_ops;
|
||||
}
|
||||
return dentry;
|
||||
}
|
||||
@ -1837,35 +1838,50 @@ struct dentry *d_alloc_name(struct dentry *parent, const char *name)
|
||||
}
|
||||
EXPORT_SYMBOL(d_alloc_name);
|
||||
|
||||
void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op)
|
||||
{
|
||||
WARN_ON_ONCE(dentry->d_op);
|
||||
WARN_ON_ONCE(dentry->d_flags & (DCACHE_OP_HASH |
|
||||
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;
|
||||
#define DCACHE_OP_FLAGS \
|
||||
(DCACHE_OP_HASH | DCACHE_OP_COMPARE | DCACHE_OP_REVALIDATE | \
|
||||
DCACHE_OP_WEAK_REVALIDATE | DCACHE_OP_DELETE | DCACHE_OP_PRUNE | \
|
||||
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;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
EXPORT_SYMBOL(d_set_d_op);
|
||||
|
||||
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)
|
||||
{
|
||||
@ -2530,13 +2546,19 @@ struct dentry *d_alloc_parallel(struct dentry *parent,
|
||||
unsigned int hash = name->hash;
|
||||
struct hlist_bl_head *b = in_lookup_hash(parent, hash);
|
||||
struct hlist_bl_node *node;
|
||||
struct dentry *new = d_alloc(parent, name);
|
||||
struct dentry *new = __d_alloc(parent->d_sb, name);
|
||||
struct dentry *dentry;
|
||||
unsigned seq, r_seq, d_seq;
|
||||
|
||||
if (unlikely(!new))
|
||||
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:
|
||||
rcu_read_lock();
|
||||
seq = smp_load_acquire(&parent->d_inode->i_dir_seq);
|
||||
@ -2620,8 +2642,6 @@ retry:
|
||||
return dentry;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
/* we can't take ->d_lock here; it's OK, though. */
|
||||
new->d_flags |= DCACHE_PAR_LOOKUP;
|
||||
new->d_wait = wq;
|
||||
hlist_bl_add_head(&new->d_u.d_in_lookup_hash, b);
|
||||
hlist_bl_unlock(b);
|
||||
@ -2667,7 +2687,8 @@ EXPORT_SYMBOL(__d_lookup_unhash_wake);
|
||||
|
||||
/* 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;
|
||||
struct inode *dir = NULL;
|
||||
@ -2678,6 +2699,8 @@ static inline void __d_add(struct dentry *dentry, struct inode *inode)
|
||||
n = start_dir_add(dir);
|
||||
d_wait = __d_lookup_unhash(dentry);
|
||||
}
|
||||
if (unlikely(ops))
|
||||
d_set_d_op(dentry, ops);
|
||||
if (inode) {
|
||||
unsigned add_flags = d_flags_for_inode(inode);
|
||||
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);
|
||||
spin_lock(&inode->i_lock);
|
||||
}
|
||||
__d_add(entry, inode);
|
||||
__d_add(entry, inode, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(d_add);
|
||||
|
||||
@ -2961,30 +2984,8 @@ out_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
struct dentry *d_splice_alias_ops(struct inode *inode, struct dentry *dentry,
|
||||
const struct dentry_operations *ops)
|
||||
{
|
||||
if (IS_ERR(inode))
|
||||
return ERR_CAST(inode);
|
||||
@ -3030,9 +3031,37 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry)
|
||||
}
|
||||
}
|
||||
out:
|
||||
__d_add(dentry, inode);
|
||||
__d_add(dentry, inode, ops);
|
||||
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);
|
||||
|
||||
/*
|
||||
|
@ -258,7 +258,6 @@ static struct vfsmount *debugfs_automount(struct path *path)
|
||||
}
|
||||
|
||||
static const struct dentry_operations debugfs_dops = {
|
||||
.d_delete = always_delete_dentry,
|
||||
.d_release = debugfs_release_dentry,
|
||||
.d_automount = debugfs_automount,
|
||||
};
|
||||
@ -273,7 +272,8 @@ static int debugfs_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
return err;
|
||||
|
||||
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);
|
||||
|
||||
|
@ -381,7 +381,7 @@ static int devpts_fill_super(struct super_block *s, struct fs_context *fc)
|
||||
s->s_blocksize_bits = 10;
|
||||
s->s_magic = DEVPTS_SUPER_MAGIC;
|
||||
s->s_op = &devpts_sops;
|
||||
s->s_d_op = &simple_dentry_operations;
|
||||
s->s_d_flags = DCACHE_DONTCACHE;
|
||||
s->s_time_gran = 1;
|
||||
fsi->sb = s;
|
||||
|
||||
|
@ -471,7 +471,7 @@ static int ecryptfs_get_tree(struct fs_context *fc)
|
||||
sbi = NULL;
|
||||
s->s_op = &ecryptfs_sops;
|
||||
s->s_xattr = ecryptfs_xattr_handlers;
|
||||
s->s_d_op = &ecryptfs_dops;
|
||||
set_default_d_op(s, &ecryptfs_dops);
|
||||
|
||||
err = "Reading sb failed";
|
||||
rc = kern_path(fc->source, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path);
|
||||
|
@ -183,7 +183,6 @@ static int efivarfs_d_hash(const struct dentry *dentry, struct qstr *qstr)
|
||||
static const struct dentry_operations efivarfs_d_ops = {
|
||||
.d_compare = efivarfs_d_compare,
|
||||
.d_hash = efivarfs_d_hash,
|
||||
.d_delete = always_delete_dentry,
|
||||
};
|
||||
|
||||
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_magic = EFIVARFS_MAGIC;
|
||||
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;
|
||||
|
||||
if (!efivar_supports_writes())
|
||||
|
@ -667,9 +667,9 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
}
|
||||
|
||||
if (sbi->options.utf8)
|
||||
sb->s_d_op = &exfat_utf8_dentry_ops;
|
||||
set_default_d_op(sb, &exfat_utf8_dentry_ops);
|
||||
else
|
||||
sb->s_d_op = &exfat_dentry_ops;
|
||||
set_default_d_op(sb, &exfat_dentry_ops);
|
||||
|
||||
root_inode = new_inode(sb);
|
||||
if (!root_inode) {
|
||||
|
@ -646,7 +646,7 @@ static const struct inode_operations msdos_dir_inode_operations = {
|
||||
static void setup(struct super_block *sb)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1187,9 +1187,9 @@ static void setup(struct super_block *sb)
|
||||
{
|
||||
MSDOS_SB(sb)->dir_ops = &vfat_dir_inode_operations;
|
||||
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
|
||||
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)
|
||||
|
@ -338,13 +338,6 @@ const struct dentry_operations fuse_dentry_operations = {
|
||||
.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)
|
||||
{
|
||||
return S_ISREG(m) || S_ISDIR(m) || S_ISLNK(m) || S_ISCHR(m) ||
|
||||
|
@ -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 dentry_operations fuse_dentry_operations;
|
||||
extern const struct dentry_operations fuse_root_dentry_operations;
|
||||
|
||||
/**
|
||||
* Get a filled in inode
|
||||
|
@ -1719,7 +1719,7 @@ static int fuse_fill_super_submount(struct super_block *sb,
|
||||
fi = get_fuse_inode(root);
|
||||
fi->nlookup--;
|
||||
|
||||
sb->s_d_op = &fuse_dentry_operations;
|
||||
set_default_d_op(sb, &fuse_dentry_operations);
|
||||
sb->s_root = d_make_root(root);
|
||||
if (!sb->s_root)
|
||||
return -ENOMEM;
|
||||
@ -1854,12 +1854,10 @@ int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx)
|
||||
|
||||
err = -ENOMEM;
|
||||
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);
|
||||
if (!root_dentry)
|
||||
goto err_dev_free;
|
||||
/* Root dentry doesn't have .d_revalidate */
|
||||
sb->s_d_op = &fuse_dentry_operations;
|
||||
|
||||
mutex_lock(&fuse_mutex);
|
||||
err = -EINVAL;
|
||||
|
@ -1145,7 +1145,7 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
sb->s_magic = GFS2_MAGIC;
|
||||
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_qcop = &gfs2_quotactl_ops;
|
||||
sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP;
|
||||
|
@ -365,7 +365,7 @@ static int hfs_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
if (!root_inode)
|
||||
goto bail_no_root;
|
||||
|
||||
sb->s_d_op = &hfs_dentry_operations;
|
||||
set_default_d_op(sb, &hfs_dentry_operations);
|
||||
res = -ENOMEM;
|
||||
sb->s_root = d_make_root(root_inode);
|
||||
if (!sb->s_root)
|
||||
|
@ -508,7 +508,7 @@ static int hfsplus_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
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);
|
||||
if (!sb->s_root) {
|
||||
err = -ENOMEM;
|
||||
|
@ -933,7 +933,7 @@ static int hostfs_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
sb->s_blocksize_bits = 10;
|
||||
sb->s_magic = HOSTFS_SUPER_MAGIC;
|
||||
sb->s_op = &hostfs_sbops;
|
||||
sb->s_d_op = &simple_dentry_operations;
|
||||
sb->s_d_flags = DCACHE_DONTCACHE;
|
||||
sb->s_maxbytes = MAX_LFS_FILESIZE;
|
||||
err = super_setup_bdi(sb);
|
||||
if (err)
|
||||
|
@ -554,7 +554,7 @@ static int hpfs_fill_super(struct super_block *s, struct fs_context *fc)
|
||||
/* Fill superblock stuff */
|
||||
s->s_magic = HPFS_SUPER_MAGIC;
|
||||
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_max = local_to_gmt(s, U32_MAX);
|
||||
|
||||
|
@ -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_magic = HUGETLBFS_MAGIC;
|
||||
sb->s_op = &hugetlbfs_ops;
|
||||
sb->s_d_flags = DCACHE_DONTCACHE;
|
||||
sb->s_time_gran = 1;
|
||||
|
||||
/*
|
||||
|
@ -939,7 +939,7 @@ root_found:
|
||||
sbi->s_check = opt->check;
|
||||
|
||||
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 */
|
||||
s->s_root = d_make_root(inode);
|
||||
|
@ -542,7 +542,7 @@ static int jfs_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
sb->s_magic = JFS_SUPER_MAGIC;
|
||||
|
||||
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);
|
||||
if (IS_ERR(inode)) {
|
||||
|
@ -318,7 +318,7 @@ static int kernfs_fill_super(struct super_block *sb, struct kernfs_fs_context *k
|
||||
return -ENOMEM;
|
||||
}
|
||||
sb->s_root = root;
|
||||
sb->s_d_op = &kernfs_dops;
|
||||
set_default_d_op(sb, &kernfs_dops);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
27
fs/libfs.c
27
fs/libfs.c
@ -62,11 +62,6 @@ int always_delete_dentry(const struct dentry *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
|
||||
* 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)
|
||||
return ERR_PTR(-ENAMETOOLONG);
|
||||
if (!dentry->d_sb->s_d_op)
|
||||
d_set_d_op(dentry, &simple_dentry_operations);
|
||||
|
||||
if (!dentry->d_op && !(dentry->d_flags & DCACHE_DONTCACHE)) {
|
||||
spin_lock(&dentry->d_lock);
|
||||
dentry->d_flags |= DCACHE_DONTCACHE;
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir))
|
||||
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);
|
||||
if (!s->s_root)
|
||||
return -ENOMEM;
|
||||
s->s_d_op = ctx->dops;
|
||||
set_default_d_op(s, ctx->dops);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1948,22 +1945,22 @@ static const struct dentry_operations generic_encrypted_dentry_ops = {
|
||||
* @sb: superblock to be configured
|
||||
*
|
||||
* Filesystems supporting casefolding and/or fscrypt can call this
|
||||
* helper at mount-time to configure sb->s_d_op to best set of dentry
|
||||
* operations required for the enabled features. The helper must be
|
||||
* called after these have been configured, but before the root dentry
|
||||
* is created.
|
||||
* helper at mount-time to configure default dentry_operations to the
|
||||
* best set of dentry operations required for the enabled features.
|
||||
* The helper must be called after these have been configured, but
|
||||
* before the root dentry is created.
|
||||
*/
|
||||
void generic_set_sb_d_ops(struct super_block *sb)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_UNICODE)
|
||||
if (sb->s_encoding) {
|
||||
sb->s_d_op = &generic_ci_dentry_ops;
|
||||
set_default_d_op(sb, &generic_ci_dentry_ops);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
if (sb->s_cop) {
|
||||
sb->s_d_op = &generic_encrypted_dentry_ops;
|
||||
set_default_d_op(sb, &generic_encrypted_dentry_ops);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
@ -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;
|
||||
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);
|
||||
if (ret == 0)
|
||||
server->s_dev = s->s_dev;
|
||||
|
@ -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_time_gran = NTFS_TIME_GRAN; // 100 nsec
|
||||
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);
|
||||
if (IS_ERR(options->nls)) {
|
||||
|
@ -1962,7 +1962,7 @@ static int ocfs2_initialize_super(struct super_block *sb,
|
||||
|
||||
sb->s_fs_info = osb;
|
||||
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_qcop = &dquot_quotactl_sysfile_ops;
|
||||
sb->dq_op = &ocfs2_quota_operations;
|
||||
|
@ -416,7 +416,7 @@ static int orangefs_fill_sb(struct super_block *sb,
|
||||
sb->s_xattr = orangefs_xattr_handlers;
|
||||
sb->s_magic = ORANGEFS_SUPER_MAGIC;
|
||||
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_bits = PAGE_SHIFT;
|
||||
|
@ -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()))
|
||||
goto out_err;
|
||||
|
||||
sb->s_d_op = &ovl_dentry_operations;
|
||||
set_default_d_op(sb, &ovl_dentry_operations);
|
||||
|
||||
err = -ENOMEM;
|
||||
if (!ofs->creator_cred)
|
||||
|
@ -2704,8 +2704,7 @@ static struct dentry *proc_pident_instantiate(struct dentry *dentry,
|
||||
inode->i_fop = p->fop;
|
||||
ei->op = p->op;
|
||||
pid_update_inode(task, inode);
|
||||
d_set_d_op(dentry, &pid_dentry_operations);
|
||||
return d_splice_alias(inode, dentry);
|
||||
return d_splice_alias_ops(inode, dentry, &pid_dentry_operations);
|
||||
}
|
||||
|
||||
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);
|
||||
pid_update_inode(task, inode);
|
||||
|
||||
d_set_d_op(dentry, &pid_dentry_operations);
|
||||
return d_splice_alias(inode, dentry);
|
||||
return d_splice_alias_ops(inode, dentry, &pid_dentry_operations);
|
||||
}
|
||||
|
||||
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);
|
||||
pid_update_inode(task, inode);
|
||||
|
||||
d_set_d_op(dentry, &pid_dentry_operations);
|
||||
return d_splice_alias(inode, dentry);
|
||||
return d_splice_alias_ops(inode, dentry, &pid_dentry_operations);
|
||||
}
|
||||
|
||||
static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry, unsigned int flags)
|
||||
|
@ -254,8 +254,11 @@ struct dentry *proc_lookup_de(struct inode *dir, struct dentry *dentry,
|
||||
inode = proc_get_inode(dir->i_sb, de);
|
||||
if (!inode)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
d_set_d_op(dentry, de->proc_dops);
|
||||
return d_splice_alias(inode, dentry);
|
||||
if (de->flags & PROC_ENTRY_FORCE_LOOKUP)
|
||||
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);
|
||||
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);
|
||||
proc_set_user(ent, (*parent)->uid, (*parent)->gid);
|
||||
|
||||
ent->proc_dops = &proc_misc_dentry_ops;
|
||||
/* 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);
|
||||
|
||||
out:
|
||||
|
@ -44,7 +44,6 @@ struct proc_dir_entry {
|
||||
const struct proc_ops *proc_ops;
|
||||
const struct file_operations *proc_dir_ops;
|
||||
};
|
||||
const struct dentry_operations *proc_dops;
|
||||
union {
|
||||
const struct seq_operations *seq_ops;
|
||||
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)
|
||||
{
|
||||
/* /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,
|
||||
struct dentry *dentry, const struct dentry_operations *d_ops)
|
||||
{
|
||||
d_set_d_op(dentry, d_ops);
|
||||
dont_mount(dentry);
|
||||
return d_splice_alias(inode, dentry);
|
||||
return d_splice_alias_ops(inode, dentry, d_ops);
|
||||
}
|
||||
|
@ -111,8 +111,7 @@ static struct dentry *proc_ns_instantiate(struct dentry *dentry,
|
||||
ei->ns_ops = ns_ops;
|
||||
pid_update_inode(task, inode);
|
||||
|
||||
d_set_d_op(dentry, &pid_dentry_operations);
|
||||
return d_splice_alias(inode, dentry);
|
||||
return d_splice_alias_ops(inode, dentry, &pid_dentry_operations);
|
||||
}
|
||||
|
||||
static int proc_ns_dir_readdir(struct file *file, struct dir_context *ctx)
|
||||
|
@ -540,9 +540,8 @@ static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,
|
||||
goto out;
|
||||
}
|
||||
|
||||
d_set_d_op(dentry, &proc_sys_dentry_operations);
|
||||
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:
|
||||
if (h)
|
||||
@ -699,9 +698,9 @@ static bool proc_sys_fill_cache(struct file *file,
|
||||
return false;
|
||||
if (d_in_lookup(child)) {
|
||||
struct dentry *res;
|
||||
d_set_d_op(child, &proc_sys_dentry_operations);
|
||||
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);
|
||||
if (unlikely(res)) {
|
||||
dput(child);
|
||||
|
@ -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_magic = RAMFS_MAGIC;
|
||||
sb->s_op = &ramfs_ops;
|
||||
sb->s_d_flags = DCACHE_DONTCACHE;
|
||||
sb->s_time_gran = 1;
|
||||
|
||||
inode = ramfs_get_inode(sb, NULL, S_IFDIR | fsi->mount_opts.mode, 0);
|
||||
|
@ -260,9 +260,9 @@ cifs_read_super(struct super_block *sb)
|
||||
}
|
||||
|
||||
if (tcon->nocase)
|
||||
sb->s_d_op = &cifs_ci_dentry_ops;
|
||||
set_default_d_op(sb, &cifs_ci_dentry_ops);
|
||||
else
|
||||
sb->s_d_op = &cifs_dentry_ops;
|
||||
set_default_d_op(sb, &cifs_dentry_ops);
|
||||
|
||||
sb->s_root = d_make_root(inode);
|
||||
if (!sb->s_root) {
|
||||
|
@ -465,9 +465,20 @@ static int tracefs_d_revalidate(struct inode *inode, const struct qstr *name,
|
||||
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 = {
|
||||
.d_revalidate = tracefs_d_revalidate,
|
||||
.d_release = tracefs_d_release,
|
||||
.d_delete = tracefs_d_delete,
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
sb->s_op = &tracefs_super_operations;
|
||||
sb->s_d_op = &tracefs_dentry_operations;
|
||||
set_default_d_op(sb, &tracefs_dentry_operations);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -189,7 +189,7 @@ static int vboxsf_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
sb->s_blocksize = 1024;
|
||||
sb->s_maxbytes = MAX_LFS_FILESIZE;
|
||||
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);
|
||||
if (!iroot) {
|
||||
|
@ -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_delete(struct dentry *);
|
||||
extern void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op);
|
||||
|
||||
/* allocate/de-allocate */
|
||||
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 *,
|
||||
wait_queue_head_t *);
|
||||
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 bool d_same_name(const struct dentry *dentry, const struct dentry *parent,
|
||||
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);
|
||||
}
|
||||
|
||||
void set_default_d_op(struct super_block *, const struct dentry_operations *);
|
||||
|
||||
#endif /* __LINUX_DCACHE_H */
|
||||
|
@ -1402,6 +1402,7 @@ struct super_block {
|
||||
char s_sysfs_name[UUID_STRING_LEN + 1];
|
||||
|
||||
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
|
||||
@ -1415,7 +1416,7 @@ struct super_block {
|
||||
*/
|
||||
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 */
|
||||
|
||||
@ -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,
|
||||
const struct inode *context_inode);
|
||||
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 ssize_t generic_read_dir(struct file *, char __user *, size_t, loff_t *);
|
||||
|
@ -27,6 +27,8 @@ enum {
|
||||
|
||||
PROC_ENTRY_proc_read_iter = 1U << 1,
|
||||
PROC_ENTRY_proc_compat_ioctl = 1U << 2,
|
||||
|
||||
PROC_ENTRY_FORCE_LOOKUP = 1U << 7,
|
||||
};
|
||||
|
||||
struct proc_ops {
|
||||
|
@ -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_magic = MQUEUE_MAGIC;
|
||||
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);
|
||||
if (IS_ERR(inode))
|
||||
|
@ -4984,7 +4984,6 @@ static void shmem_put_super(struct super_block *sb)
|
||||
static const struct dentry_operations shmem_ci_dentry_ops = {
|
||||
.d_hash = generic_ci_d_hash,
|
||||
.d_compare = generic_ci_d_compare,
|
||||
.d_delete = always_delete_dentry,
|
||||
};
|
||||
#endif
|
||||
|
||||
@ -5032,7 +5031,7 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
|
||||
if (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)
|
||||
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
|
||||
sb->s_flags |= SB_NOUSER;
|
||||
#endif /* CONFIG_TMPFS */
|
||||
sb->s_d_flags |= DCACHE_DONTCACHE;
|
||||
sbinfo->max_blocks = ctx->blocks;
|
||||
sbinfo->max_inodes = ctx->inodes;
|
||||
sbinfo->free_ispace = sbinfo->max_inodes * BOGO_INODE_SIZE;
|
||||
|
@ -1363,7 +1363,7 @@ rpc_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
sb->s_blocksize_bits = PAGE_SHIFT;
|
||||
sb->s_magic = RPCAUTH_GSSMAGIC;
|
||||
sb->s_op = &s_ops;
|
||||
sb->s_d_op = &simple_dentry_operations;
|
||||
sb->s_d_flags = DCACHE_DONTCACHE;
|
||||
sb->s_time_gran = 1;
|
||||
|
||||
inode = rpc_get_inode(sb, S_IFDIR | 0555);
|
||||
|
Loading…
Reference in New Issue
Block a user