mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-18 22:14:16 +00:00
Provide stable parent and name to ->d_revalidate() instances
Most of the filesystem methods where we care about dentry name and parent have their stability guaranteed by the callers; ->d_revalidate() is the major exception. It's easy enough for callers to supply stable values for expected name and expected parent of the dentry being validated. That kills quite a bit of boilerplate in ->d_revalidate() instances, along with a bunch of races where they used to access ->d_name without sufficient precautions. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQQqUNBr3gm4hGXdBJlZ7Krx/gZQ6wUCZ5gkoQAKCRBZ7Krx/gZQ 6w9FAP4nyxNNWMjE1TwuWR/DNDMYYuw/qn/miZ88B5BUM8hzqgD/W2SjRvcbSaIm xSIYpbtKgtqNU34P1PU+dBvL8Utz2AE= =TWY8 -----END PGP SIGNATURE----- Merge tag 'pull-revalidate' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs Pull vfs d_revalidate updates from Al Viro: "Provide stable parent and name to ->d_revalidate() instances Most of the filesystem methods where we care about dentry name and parent have their stability guaranteed by the callers; ->d_revalidate() is the major exception. It's easy enough for callers to supply stable values for expected name and expected parent of the dentry being validated. That kills quite a bit of boilerplate in ->d_revalidate() instances, along with a bunch of races where they used to access ->d_name without sufficient precautions" * tag 'pull-revalidate' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: 9p: fix ->rename_sem exclusion orangefs_d_revalidate(): use stable parent inode and name passed by caller ocfs2_dentry_revalidate(): use stable parent inode and name passed by caller nfs: fix ->d_revalidate() UAF on ->d_name accesses nfs{,4}_lookup_validate(): use stable parent inode passed by caller gfs2_drevalidate(): use stable parent inode and name passed by caller fuse_dentry_revalidate(): use stable parent inode and name passed by caller vfat_revalidate{,_ci}(): use stable parent inode passed by caller exfat_d_revalidate(): use stable parent inode passed by caller fscrypt_d_revalidate(): use stable parent inode passed by caller ceph_d_revalidate(): propagate stable name down into request encoding ceph_d_revalidate(): use stable parent inode passed by caller afs_d_revalidate(): use stable name and parent inode passed by caller Pass parent directory inode and expected name to ->d_revalidate() generic_ci_d_compare(): use shortname_storage ext4 fast_commit: make use of name_snapshot primitives dissolve external_name.u into separate members make take_dentry_name_snapshot() lockless dcache: back inline names with a struct-wrapped array of unsigned long make sure that DNAME_INLINE_LEN is a multiple of word size
This commit is contained in:
commit
d3d90cc289
43 changed files with 359 additions and 307 deletions
|
@ -17,7 +17,8 @@ dentry_operations
|
||||||
|
|
||||||
prototypes::
|
prototypes::
|
||||||
|
|
||||||
int (*d_revalidate)(struct dentry *, unsigned int);
|
int (*d_revalidate)(struct inode *, const struct qstr *,
|
||||||
|
struct dentry *, unsigned int);
|
||||||
int (*d_weak_revalidate)(struct dentry *, unsigned int);
|
int (*d_weak_revalidate)(struct dentry *, unsigned int);
|
||||||
int (*d_hash)(const struct dentry *, struct qstr *);
|
int (*d_hash)(const struct dentry *, struct qstr *);
|
||||||
int (*d_compare)(const struct dentry *,
|
int (*d_compare)(const struct dentry *,
|
||||||
|
@ -30,6 +31,8 @@ prototypes::
|
||||||
struct vfsmount *(*d_automount)(struct path *path);
|
struct vfsmount *(*d_automount)(struct path *path);
|
||||||
int (*d_manage)(const struct path *, bool);
|
int (*d_manage)(const struct path *, bool);
|
||||||
struct dentry *(*d_real)(struct dentry *, enum d_real_type type);
|
struct dentry *(*d_real)(struct dentry *, enum d_real_type type);
|
||||||
|
bool (*d_unalias_trylock)(const struct dentry *);
|
||||||
|
void (*d_unalias_unlock)(const struct dentry *);
|
||||||
|
|
||||||
locking rules:
|
locking rules:
|
||||||
|
|
||||||
|
@ -49,6 +52,8 @@ d_dname: no no no no
|
||||||
d_automount: no no yes no
|
d_automount: no no yes no
|
||||||
d_manage: no no yes (ref-walk) maybe
|
d_manage: no no yes (ref-walk) maybe
|
||||||
d_real no no yes no
|
d_real no no yes no
|
||||||
|
d_unalias_trylock yes no no no
|
||||||
|
d_unalias_unlock yes no no no
|
||||||
================== =========== ======== ============== ========
|
================== =========== ======== ============== ========
|
||||||
|
|
||||||
inode_operations
|
inode_operations
|
||||||
|
|
|
@ -1141,3 +1141,19 @@ pointer are gone.
|
||||||
|
|
||||||
set_blocksize() takes opened struct file instead of struct block_device now
|
set_blocksize() takes opened struct file instead of struct block_device now
|
||||||
and it *must* be opened exclusive.
|
and it *must* be opened exclusive.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
** mandatory**
|
||||||
|
|
||||||
|
->d_revalidate() gets two extra arguments - inode of parent directory and
|
||||||
|
name our dentry is expected to have. Both are stable (dir is pinned in
|
||||||
|
non-RCU case and will stay around during the call in RCU case, and name
|
||||||
|
is guaranteed to stay unchanging). Your instance doesn't have to use
|
||||||
|
either, but it often helps to avoid a lot of painful boilerplate.
|
||||||
|
Note that while name->name is stable and NUL-terminated, it may (and
|
||||||
|
often will) have name->name[name->len] equal to '/' rather than '\0' -
|
||||||
|
in normal case it points into the pathname being looked up.
|
||||||
|
NOTE: if you need something like full path from the root of filesystem,
|
||||||
|
you are still on your own - this assists with simple cases, but it's not
|
||||||
|
magic.
|
||||||
|
|
|
@ -1251,7 +1251,8 @@ defined:
|
||||||
.. code-block:: c
|
.. code-block:: c
|
||||||
|
|
||||||
struct dentry_operations {
|
struct dentry_operations {
|
||||||
int (*d_revalidate)(struct dentry *, unsigned int);
|
int (*d_revalidate)(struct inode *, const struct qstr *,
|
||||||
|
struct dentry *, unsigned int);
|
||||||
int (*d_weak_revalidate)(struct dentry *, unsigned int);
|
int (*d_weak_revalidate)(struct dentry *, unsigned int);
|
||||||
int (*d_hash)(const struct dentry *, struct qstr *);
|
int (*d_hash)(const struct dentry *, struct qstr *);
|
||||||
int (*d_compare)(const struct dentry *,
|
int (*d_compare)(const struct dentry *,
|
||||||
|
@ -1264,6 +1265,8 @@ defined:
|
||||||
struct vfsmount *(*d_automount)(struct path *);
|
struct vfsmount *(*d_automount)(struct path *);
|
||||||
int (*d_manage)(const struct path *, bool);
|
int (*d_manage)(const struct path *, bool);
|
||||||
struct dentry *(*d_real)(struct dentry *, enum d_real_type type);
|
struct dentry *(*d_real)(struct dentry *, enum d_real_type type);
|
||||||
|
bool (*d_unalias_trylock)(const struct dentry *);
|
||||||
|
void (*d_unalias_unlock)(const struct dentry *);
|
||||||
};
|
};
|
||||||
|
|
||||||
``d_revalidate``
|
``d_revalidate``
|
||||||
|
@ -1427,6 +1430,25 @@ defined:
|
||||||
|
|
||||||
For non-regular files, the 'dentry' argument is returned.
|
For non-regular files, the 'dentry' argument is returned.
|
||||||
|
|
||||||
|
``d_unalias_trylock``
|
||||||
|
if present, will be called by d_splice_alias() before moving a
|
||||||
|
preexisting attached alias. Returning false prevents __d_move(),
|
||||||
|
making d_splice_alias() fail with -ESTALE.
|
||||||
|
|
||||||
|
Rationale: setting FS_RENAME_DOES_D_MOVE will prevent d_move()
|
||||||
|
and d_exchange() calls from the outside of filesystem methods;
|
||||||
|
however, it does not guarantee that attached dentries won't
|
||||||
|
be renamed or moved by d_splice_alias() finding a preexisting
|
||||||
|
alias for a directory inode. Normally we would not care;
|
||||||
|
however, something that wants to stabilize the entire path to
|
||||||
|
root over a blocking operation might need that. See 9p for one
|
||||||
|
(and hopefully only) example.
|
||||||
|
|
||||||
|
``d_unalias_unlock``
|
||||||
|
should be paired with ``d_unalias_trylock``; that one is called after
|
||||||
|
__d_move() call in __d_unalias().
|
||||||
|
|
||||||
|
|
||||||
Each dentry has a pointer to its parent dentry, as well as a hash list
|
Each dentry has a pointer to its parent dentry, as well as a hash list
|
||||||
of child dentries. Child dentries are basically like files in a
|
of child dentries. Child dentries are basically like files in a
|
||||||
directory.
|
directory.
|
||||||
|
|
|
@ -202,7 +202,7 @@ static inline struct v9fs_session_info *v9fs_inode2v9ses(struct inode *inode)
|
||||||
return inode->i_sb->s_fs_info;
|
return inode->i_sb->s_fs_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct v9fs_session_info *v9fs_dentry2v9ses(struct dentry *dentry)
|
static inline struct v9fs_session_info *v9fs_dentry2v9ses(const struct dentry *dentry)
|
||||||
{
|
{
|
||||||
return dentry->d_sb->s_fs_info;
|
return dentry->d_sb->s_fs_info;
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ static void v9fs_dentry_release(struct dentry *dentry)
|
||||||
p9_fid_put(hlist_entry(p, struct p9_fid, dlist));
|
p9_fid_put(hlist_entry(p, struct p9_fid, dlist));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int v9fs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
|
static int __v9fs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
struct p9_fid *fid;
|
struct p9_fid *fid;
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
|
@ -99,14 +99,36 @@ out_valid:
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int v9fs_lookup_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *dentry, unsigned int flags)
|
||||||
|
{
|
||||||
|
return __v9fs_lookup_revalidate(dentry, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool v9fs_dentry_unalias_trylock(const struct dentry *dentry)
|
||||||
|
{
|
||||||
|
struct v9fs_session_info *v9ses = v9fs_dentry2v9ses(dentry);
|
||||||
|
return down_write_trylock(&v9ses->rename_sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void v9fs_dentry_unalias_unlock(const struct dentry *dentry)
|
||||||
|
{
|
||||||
|
struct v9fs_session_info *v9ses = v9fs_dentry2v9ses(dentry);
|
||||||
|
up_write(&v9ses->rename_sem);
|
||||||
|
}
|
||||||
|
|
||||||
const struct dentry_operations v9fs_cached_dentry_operations = {
|
const struct dentry_operations v9fs_cached_dentry_operations = {
|
||||||
.d_revalidate = v9fs_lookup_revalidate,
|
.d_revalidate = v9fs_lookup_revalidate,
|
||||||
.d_weak_revalidate = v9fs_lookup_revalidate,
|
.d_weak_revalidate = __v9fs_lookup_revalidate,
|
||||||
.d_delete = v9fs_cached_dentry_delete,
|
.d_delete = v9fs_cached_dentry_delete,
|
||||||
.d_release = v9fs_dentry_release,
|
.d_release = v9fs_dentry_release,
|
||||||
|
.d_unalias_trylock = v9fs_dentry_unalias_trylock,
|
||||||
|
.d_unalias_unlock = v9fs_dentry_unalias_unlock,
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct dentry_operations v9fs_dentry_operations = {
|
const struct dentry_operations v9fs_dentry_operations = {
|
||||||
.d_delete = always_delete_dentry,
|
.d_delete = always_delete_dentry,
|
||||||
.d_release = v9fs_dentry_release,
|
.d_release = v9fs_dentry_release,
|
||||||
|
.d_unalias_trylock = v9fs_dentry_unalias_trylock,
|
||||||
|
.d_unalias_unlock = v9fs_dentry_unalias_unlock,
|
||||||
};
|
};
|
||||||
|
|
40
fs/afs/dir.c
40
fs/afs/dir.c
|
@ -23,7 +23,8 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
|
||||||
unsigned int flags);
|
unsigned int flags);
|
||||||
static int afs_dir_open(struct inode *inode, struct file *file);
|
static int afs_dir_open(struct inode *inode, struct file *file);
|
||||||
static int afs_readdir(struct file *file, struct dir_context *ctx);
|
static int afs_readdir(struct file *file, struct dir_context *ctx);
|
||||||
static int afs_d_revalidate(struct dentry *dentry, unsigned int flags);
|
static int afs_d_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *dentry, unsigned int flags);
|
||||||
static int afs_d_delete(const struct dentry *dentry);
|
static int afs_d_delete(const struct dentry *dentry);
|
||||||
static void afs_d_iput(struct dentry *dentry, struct inode *inode);
|
static void afs_d_iput(struct dentry *dentry, struct inode *inode);
|
||||||
static bool afs_lookup_one_filldir(struct dir_context *ctx, const char *name, int nlen,
|
static bool afs_lookup_one_filldir(struct dir_context *ctx, const char *name, int nlen,
|
||||||
|
@ -597,19 +598,19 @@ static bool afs_lookup_one_filldir(struct dir_context *ctx, const char *name,
|
||||||
* Do a lookup of a single name in a directory
|
* Do a lookup of a single name in a directory
|
||||||
* - just returns the FID the dentry name maps to if found
|
* - just returns the FID the dentry name maps to if found
|
||||||
*/
|
*/
|
||||||
static int afs_do_lookup_one(struct inode *dir, struct dentry *dentry,
|
static int afs_do_lookup_one(struct inode *dir, const struct qstr *name,
|
||||||
struct afs_fid *fid,
|
struct afs_fid *fid,
|
||||||
afs_dataversion_t *_dir_version)
|
afs_dataversion_t *_dir_version)
|
||||||
{
|
{
|
||||||
struct afs_super_info *as = dir->i_sb->s_fs_info;
|
struct afs_super_info *as = dir->i_sb->s_fs_info;
|
||||||
struct afs_lookup_one_cookie cookie = {
|
struct afs_lookup_one_cookie cookie = {
|
||||||
.ctx.actor = afs_lookup_one_filldir,
|
.ctx.actor = afs_lookup_one_filldir,
|
||||||
.name = dentry->d_name,
|
.name = *name,
|
||||||
.fid.vid = as->volume->vid
|
.fid.vid = as->volume->vid
|
||||||
};
|
};
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
_enter("{%lu},%p{%pd},", dir->i_ino, dentry, dentry);
|
_enter("{%lu},{%.*s},", dir->i_ino, name->len, name->name);
|
||||||
|
|
||||||
/* search the directory */
|
/* search the directory */
|
||||||
ret = afs_dir_iterate(dir, &cookie.ctx, NULL, _dir_version);
|
ret = afs_dir_iterate(dir, &cookie.ctx, NULL, _dir_version);
|
||||||
|
@ -1023,21 +1024,12 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
|
||||||
/*
|
/*
|
||||||
* Check the validity of a dentry under RCU conditions.
|
* Check the validity of a dentry under RCU conditions.
|
||||||
*/
|
*/
|
||||||
static int afs_d_revalidate_rcu(struct dentry *dentry)
|
static int afs_d_revalidate_rcu(struct afs_vnode *dvnode, struct dentry *dentry)
|
||||||
{
|
{
|
||||||
struct afs_vnode *dvnode;
|
|
||||||
struct dentry *parent;
|
|
||||||
struct inode *dir;
|
|
||||||
long dir_version, de_version;
|
long dir_version, de_version;
|
||||||
|
|
||||||
_enter("%p", dentry);
|
_enter("%p", dentry);
|
||||||
|
|
||||||
/* Check the parent directory is still valid first. */
|
|
||||||
parent = READ_ONCE(dentry->d_parent);
|
|
||||||
dir = d_inode_rcu(parent);
|
|
||||||
if (!dir)
|
|
||||||
return -ECHILD;
|
|
||||||
dvnode = AFS_FS_I(dir);
|
|
||||||
if (test_bit(AFS_VNODE_DELETED, &dvnode->flags))
|
if (test_bit(AFS_VNODE_DELETED, &dvnode->flags))
|
||||||
return -ECHILD;
|
return -ECHILD;
|
||||||
|
|
||||||
|
@ -1065,11 +1057,11 @@ static int afs_d_revalidate_rcu(struct dentry *dentry)
|
||||||
* - NOTE! the hit can be a negative hit too, so we can't assume we have an
|
* - NOTE! the hit can be a negative hit too, so we can't assume we have an
|
||||||
* inode
|
* inode
|
||||||
*/
|
*/
|
||||||
static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
|
static int afs_d_revalidate(struct inode *parent_dir, const struct qstr *name,
|
||||||
|
struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
struct afs_vnode *vnode, *dir;
|
struct afs_vnode *vnode, *dir = AFS_FS_I(parent_dir);
|
||||||
struct afs_fid fid;
|
struct afs_fid fid;
|
||||||
struct dentry *parent;
|
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
struct key *key;
|
struct key *key;
|
||||||
afs_dataversion_t dir_version, invalid_before;
|
afs_dataversion_t dir_version, invalid_before;
|
||||||
|
@ -1077,7 +1069,7 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (flags & LOOKUP_RCU)
|
if (flags & LOOKUP_RCU)
|
||||||
return afs_d_revalidate_rcu(dentry);
|
return afs_d_revalidate_rcu(dir, dentry);
|
||||||
|
|
||||||
if (d_really_is_positive(dentry)) {
|
if (d_really_is_positive(dentry)) {
|
||||||
vnode = AFS_FS_I(d_inode(dentry));
|
vnode = AFS_FS_I(d_inode(dentry));
|
||||||
|
@ -1092,14 +1084,9 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
|
||||||
if (IS_ERR(key))
|
if (IS_ERR(key))
|
||||||
key = NULL;
|
key = NULL;
|
||||||
|
|
||||||
/* Hold the parent dentry so we can peer at it */
|
|
||||||
parent = dget_parent(dentry);
|
|
||||||
dir = AFS_FS_I(d_inode(parent));
|
|
||||||
|
|
||||||
/* validate the parent directory */
|
/* validate the parent directory */
|
||||||
ret = afs_validate(dir, key);
|
ret = afs_validate(dir, key);
|
||||||
if (ret == -ERESTARTSYS) {
|
if (ret == -ERESTARTSYS) {
|
||||||
dput(parent);
|
|
||||||
key_put(key);
|
key_put(key);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1127,7 +1114,7 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
|
||||||
afs_stat_v(dir, n_reval);
|
afs_stat_v(dir, n_reval);
|
||||||
|
|
||||||
/* search the directory for this vnode */
|
/* search the directory for this vnode */
|
||||||
ret = afs_do_lookup_one(&dir->netfs.inode, dentry, &fid, &dir_version);
|
ret = afs_do_lookup_one(&dir->netfs.inode, name, &fid, &dir_version);
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
case 0:
|
case 0:
|
||||||
/* the filename maps to something */
|
/* the filename maps to something */
|
||||||
|
@ -1171,22 +1158,19 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
|
||||||
goto out_valid;
|
goto out_valid;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
_debug("failed to iterate dir %pd: %d",
|
_debug("failed to iterate parent %pd2: %d", dentry, ret);
|
||||||
parent, ret);
|
|
||||||
goto not_found;
|
goto not_found;
|
||||||
}
|
}
|
||||||
|
|
||||||
out_valid:
|
out_valid:
|
||||||
dentry->d_fsdata = (void *)(unsigned long)dir_version;
|
dentry->d_fsdata = (void *)(unsigned long)dir_version;
|
||||||
out_valid_noupdate:
|
out_valid_noupdate:
|
||||||
dput(parent);
|
|
||||||
key_put(key);
|
key_put(key);
|
||||||
_leave(" = 1 [valid]");
|
_leave(" = 1 [valid]");
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
not_found:
|
not_found:
|
||||||
_debug("dropping dentry %pd2", dentry);
|
_debug("dropping dentry %pd2", dentry);
|
||||||
dput(parent);
|
|
||||||
key_put(key);
|
key_put(key);
|
||||||
|
|
||||||
_leave(" = 0 [bad]");
|
_leave(" = 0 [bad]");
|
||||||
|
|
|
@ -1940,29 +1940,19 @@ static int dir_lease_is_valid(struct inode *dir, struct dentry *dentry,
|
||||||
/*
|
/*
|
||||||
* Check if cached dentry can be trusted.
|
* Check if cached dentry can be trusted.
|
||||||
*/
|
*/
|
||||||
static int ceph_d_revalidate(struct dentry *dentry, unsigned int flags)
|
static int ceph_d_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(dentry->d_sb)->mdsc;
|
struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(dentry->d_sb)->mdsc;
|
||||||
struct ceph_client *cl = mdsc->fsc->client;
|
struct ceph_client *cl = mdsc->fsc->client;
|
||||||
int valid = 0;
|
int valid = 0;
|
||||||
struct dentry *parent;
|
struct inode *inode;
|
||||||
struct inode *dir, *inode;
|
|
||||||
|
|
||||||
valid = fscrypt_d_revalidate(dentry, flags);
|
valid = fscrypt_d_revalidate(dir, name, dentry, flags);
|
||||||
if (valid <= 0)
|
if (valid <= 0)
|
||||||
return valid;
|
return valid;
|
||||||
|
|
||||||
if (flags & LOOKUP_RCU) {
|
inode = d_inode_rcu(dentry);
|
||||||
parent = READ_ONCE(dentry->d_parent);
|
|
||||||
dir = d_inode_rcu(parent);
|
|
||||||
if (!dir)
|
|
||||||
return -ECHILD;
|
|
||||||
inode = d_inode_rcu(dentry);
|
|
||||||
} else {
|
|
||||||
parent = dget_parent(dentry);
|
|
||||||
dir = d_inode(parent);
|
|
||||||
inode = d_inode(dentry);
|
|
||||||
}
|
|
||||||
|
|
||||||
doutc(cl, "%p '%pd' inode %p offset 0x%llx nokey %d\n",
|
doutc(cl, "%p '%pd' inode %p offset 0x%llx nokey %d\n",
|
||||||
dentry, dentry, inode, ceph_dentry(dentry)->offset,
|
dentry, dentry, inode, ceph_dentry(dentry)->offset,
|
||||||
|
@ -2008,6 +1998,8 @@ static int ceph_d_revalidate(struct dentry *dentry, unsigned int flags)
|
||||||
req->r_parent = dir;
|
req->r_parent = dir;
|
||||||
ihold(dir);
|
ihold(dir);
|
||||||
|
|
||||||
|
req->r_dname = name;
|
||||||
|
|
||||||
mask = CEPH_STAT_CAP_INODE | CEPH_CAP_AUTH_SHARED;
|
mask = CEPH_STAT_CAP_INODE | CEPH_CAP_AUTH_SHARED;
|
||||||
if (ceph_security_xattr_wanted(dir))
|
if (ceph_security_xattr_wanted(dir))
|
||||||
mask |= CEPH_CAP_XATTR_SHARED;
|
mask |= CEPH_CAP_XATTR_SHARED;
|
||||||
|
@ -2038,9 +2030,6 @@ static int ceph_d_revalidate(struct dentry *dentry, unsigned int flags)
|
||||||
doutc(cl, "%p '%pd' %s\n", dentry, dentry, valid ? "valid" : "invalid");
|
doutc(cl, "%p '%pd' %s\n", dentry, dentry, valid ? "valid" : "invalid");
|
||||||
if (!valid)
|
if (!valid)
|
||||||
ceph_dir_clear_complete(dir);
|
ceph_dir_clear_complete(dir);
|
||||||
|
|
||||||
if (!(flags & LOOKUP_RCU))
|
|
||||||
dput(parent);
|
|
||||||
return valid;
|
return valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2621,6 +2621,7 @@ static u8 *get_fscrypt_altname(const struct ceph_mds_request *req, u32 *plen)
|
||||||
{
|
{
|
||||||
struct inode *dir = req->r_parent;
|
struct inode *dir = req->r_parent;
|
||||||
struct dentry *dentry = req->r_dentry;
|
struct dentry *dentry = req->r_dentry;
|
||||||
|
const struct qstr *name = req->r_dname;
|
||||||
u8 *cryptbuf = NULL;
|
u8 *cryptbuf = NULL;
|
||||||
u32 len = 0;
|
u32 len = 0;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
@ -2641,8 +2642,10 @@ static u8 *get_fscrypt_altname(const struct ceph_mds_request *req, u32 *plen)
|
||||||
if (!fscrypt_has_encryption_key(dir))
|
if (!fscrypt_has_encryption_key(dir))
|
||||||
goto success;
|
goto success;
|
||||||
|
|
||||||
if (!fscrypt_fname_encrypted_size(dir, dentry->d_name.len, NAME_MAX,
|
if (!name)
|
||||||
&len)) {
|
name = &dentry->d_name;
|
||||||
|
|
||||||
|
if (!fscrypt_fname_encrypted_size(dir, name->len, NAME_MAX, &len)) {
|
||||||
WARN_ON_ONCE(1);
|
WARN_ON_ONCE(1);
|
||||||
return ERR_PTR(-ENAMETOOLONG);
|
return ERR_PTR(-ENAMETOOLONG);
|
||||||
}
|
}
|
||||||
|
@ -2657,7 +2660,7 @@ static u8 *get_fscrypt_altname(const struct ceph_mds_request *req, u32 *plen)
|
||||||
if (!cryptbuf)
|
if (!cryptbuf)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
ret = fscrypt_fname_encrypt(dir, &dentry->d_name, cryptbuf, len);
|
ret = fscrypt_fname_encrypt(dir, name, cryptbuf, len);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
kfree(cryptbuf);
|
kfree(cryptbuf);
|
||||||
return ERR_PTR(ret);
|
return ERR_PTR(ret);
|
||||||
|
|
|
@ -299,6 +299,8 @@ struct ceph_mds_request {
|
||||||
struct inode *r_target_inode; /* resulting inode */
|
struct inode *r_target_inode; /* resulting inode */
|
||||||
struct inode *r_new_inode; /* new inode (for creates) */
|
struct inode *r_new_inode; /* new inode (for creates) */
|
||||||
|
|
||||||
|
const struct qstr *r_dname; /* stable name (for ->d_revalidate) */
|
||||||
|
|
||||||
#define CEPH_MDS_R_DIRECT_IS_HASH (1) /* r_direct_hash is valid */
|
#define CEPH_MDS_R_DIRECT_IS_HASH (1) /* r_direct_hash is valid */
|
||||||
#define CEPH_MDS_R_ABORTED (2) /* call was aborted */
|
#define CEPH_MDS_R_ABORTED (2) /* call was aborted */
|
||||||
#define CEPH_MDS_R_GOT_UNSAFE (3) /* got an unsafe reply */
|
#define CEPH_MDS_R_GOT_UNSAFE (3) /* got an unsafe reply */
|
||||||
|
|
|
@ -445,7 +445,8 @@ static int coda_readdir(struct file *coda_file, struct dir_context *ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* called when a cache lookup succeeds */
|
/* called when a cache lookup succeeds */
|
||||||
static int coda_dentry_revalidate(struct dentry *de, unsigned int flags)
|
static int coda_dentry_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *de, unsigned int flags)
|
||||||
{
|
{
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
struct coda_inode_info *cii;
|
struct coda_inode_info *cii;
|
||||||
|
|
|
@ -574,11 +574,10 @@ EXPORT_SYMBOL_GPL(fscrypt_fname_siphash);
|
||||||
* Validate dentries in encrypted directories to make sure we aren't potentially
|
* Validate dentries in encrypted directories to make sure we aren't potentially
|
||||||
* caching stale dentries after a key has been added.
|
* caching stale dentries after a key has been added.
|
||||||
*/
|
*/
|
||||||
int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags)
|
int fscrypt_d_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
struct dentry *dir;
|
|
||||||
int err;
|
int err;
|
||||||
int valid;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Plaintext names are always valid, since fscrypt doesn't support
|
* Plaintext names are always valid, since fscrypt doesn't support
|
||||||
|
@ -591,30 +590,21 @@ int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags)
|
||||||
/*
|
/*
|
||||||
* No-key name; valid if the directory's key is still unavailable.
|
* No-key name; valid if the directory's key is still unavailable.
|
||||||
*
|
*
|
||||||
* Although fscrypt forbids rename() on no-key names, we still must use
|
* Note in RCU mode we have to bail if we get here -
|
||||||
* dget_parent() here rather than use ->d_parent directly. That's
|
* fscrypt_get_encryption_info() may block.
|
||||||
* because a corrupted fs image may contain directory hard links, which
|
|
||||||
* the VFS handles by moving the directory's dentry tree in the dcache
|
|
||||||
* each time ->lookup() finds the directory and it already has a dentry
|
|
||||||
* elsewhere. Thus ->d_parent can be changing, and we must safely grab
|
|
||||||
* a reference to some ->d_parent to prevent it from being freed.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (flags & LOOKUP_RCU)
|
if (flags & LOOKUP_RCU)
|
||||||
return -ECHILD;
|
return -ECHILD;
|
||||||
|
|
||||||
dir = dget_parent(dentry);
|
|
||||||
/*
|
/*
|
||||||
* Pass allow_unsupported=true, so that files with an unsupported
|
* Pass allow_unsupported=true, so that files with an unsupported
|
||||||
* encryption policy can be deleted.
|
* encryption policy can be deleted.
|
||||||
*/
|
*/
|
||||||
err = fscrypt_get_encryption_info(d_inode(dir), true);
|
err = fscrypt_get_encryption_info(dir, true);
|
||||||
valid = !fscrypt_has_encryption_key(d_inode(dir));
|
|
||||||
dput(dir);
|
|
||||||
|
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
return valid;
|
return !fscrypt_has_encryption_key(dir);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(fscrypt_d_revalidate);
|
EXPORT_SYMBOL_GPL(fscrypt_d_revalidate);
|
||||||
|
|
103
fs/dcache.c
103
fs/dcache.c
|
@ -295,12 +295,16 @@ static inline int dentry_cmp(const struct dentry *dentry, const unsigned char *c
|
||||||
return dentry_string_cmp(cs, ct, tcount);
|
return dentry_string_cmp(cs, ct, tcount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* long names are allocated separately from dentry and never modified.
|
||||||
|
* Refcounted, freeing is RCU-delayed. See take_dentry_name_snapshot()
|
||||||
|
* for the reason why ->count and ->head can't be combined into a union.
|
||||||
|
* dentry_string_cmp() relies upon ->name[] being word-aligned.
|
||||||
|
*/
|
||||||
struct external_name {
|
struct external_name {
|
||||||
union {
|
atomic_t count;
|
||||||
atomic_t count;
|
struct rcu_head head;
|
||||||
struct rcu_head head;
|
unsigned char name[] __aligned(sizeof(unsigned long));
|
||||||
} u;
|
|
||||||
unsigned char name[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct external_name *external_name(struct dentry *dentry)
|
static inline struct external_name *external_name(struct dentry *dentry)
|
||||||
|
@ -324,31 +328,45 @@ static void __d_free_external(struct rcu_head *head)
|
||||||
|
|
||||||
static inline int dname_external(const struct dentry *dentry)
|
static inline int dname_external(const struct dentry *dentry)
|
||||||
{
|
{
|
||||||
return dentry->d_name.name != dentry->d_iname;
|
return dentry->d_name.name != dentry->d_shortname.string;
|
||||||
}
|
}
|
||||||
|
|
||||||
void take_dentry_name_snapshot(struct name_snapshot *name, struct dentry *dentry)
|
void take_dentry_name_snapshot(struct name_snapshot *name, struct dentry *dentry)
|
||||||
{
|
{
|
||||||
spin_lock(&dentry->d_lock);
|
unsigned seq;
|
||||||
name->name = dentry->d_name;
|
const unsigned char *s;
|
||||||
if (unlikely(dname_external(dentry))) {
|
|
||||||
atomic_inc(&external_name(dentry)->u.count);
|
rcu_read_lock();
|
||||||
|
retry:
|
||||||
|
seq = read_seqcount_begin(&dentry->d_seq);
|
||||||
|
s = READ_ONCE(dentry->d_name.name);
|
||||||
|
name->name.hash_len = dentry->d_name.hash_len;
|
||||||
|
name->name.name = name->inline_name.string;
|
||||||
|
if (likely(s == dentry->d_shortname.string)) {
|
||||||
|
name->inline_name = dentry->d_shortname;
|
||||||
} else {
|
} else {
|
||||||
memcpy(name->inline_name, dentry->d_iname,
|
struct external_name *p;
|
||||||
dentry->d_name.len + 1);
|
p = container_of(s, struct external_name, name[0]);
|
||||||
name->name.name = name->inline_name;
|
// get a valid reference
|
||||||
|
if (unlikely(!atomic_inc_not_zero(&p->count)))
|
||||||
|
goto retry;
|
||||||
|
name->name.name = s;
|
||||||
}
|
}
|
||||||
spin_unlock(&dentry->d_lock);
|
if (read_seqcount_retry(&dentry->d_seq, seq)) {
|
||||||
|
release_dentry_name_snapshot(name);
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(take_dentry_name_snapshot);
|
EXPORT_SYMBOL(take_dentry_name_snapshot);
|
||||||
|
|
||||||
void release_dentry_name_snapshot(struct name_snapshot *name)
|
void release_dentry_name_snapshot(struct name_snapshot *name)
|
||||||
{
|
{
|
||||||
if (unlikely(name->name.name != name->inline_name)) {
|
if (unlikely(name->name.name != name->inline_name.string)) {
|
||||||
struct external_name *p;
|
struct external_name *p;
|
||||||
p = container_of(name->name.name, struct external_name, name[0]);
|
p = container_of(name->name.name, struct external_name, name[0]);
|
||||||
if (unlikely(atomic_dec_and_test(&p->u.count)))
|
if (unlikely(atomic_dec_and_test(&p->count)))
|
||||||
kfree_rcu(p, u.head);
|
kfree_rcu(p, head);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(release_dentry_name_snapshot);
|
EXPORT_SYMBOL(release_dentry_name_snapshot);
|
||||||
|
@ -386,7 +404,7 @@ static void dentry_free(struct dentry *dentry)
|
||||||
WARN_ON(!hlist_unhashed(&dentry->d_u.d_alias));
|
WARN_ON(!hlist_unhashed(&dentry->d_u.d_alias));
|
||||||
if (unlikely(dname_external(dentry))) {
|
if (unlikely(dname_external(dentry))) {
|
||||||
struct external_name *p = external_name(dentry);
|
struct external_name *p = external_name(dentry);
|
||||||
if (likely(atomic_dec_and_test(&p->u.count))) {
|
if (likely(atomic_dec_and_test(&p->count))) {
|
||||||
call_rcu(&dentry->d_u.d_rcu, __d_free_external);
|
call_rcu(&dentry->d_u.d_rcu, __d_free_external);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1654,10 +1672,10 @@ static struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
|
||||||
* will still always have a NUL at the end, even if we might
|
* will still always have a NUL at the end, even if we might
|
||||||
* be overwriting an internal NUL character
|
* be overwriting an internal NUL character
|
||||||
*/
|
*/
|
||||||
dentry->d_iname[DNAME_INLINE_LEN-1] = 0;
|
dentry->d_shortname.string[DNAME_INLINE_LEN-1] = 0;
|
||||||
if (unlikely(!name)) {
|
if (unlikely(!name)) {
|
||||||
name = &slash_name;
|
name = &slash_name;
|
||||||
dname = dentry->d_iname;
|
dname = dentry->d_shortname.string;
|
||||||
} else if (name->len > DNAME_INLINE_LEN-1) {
|
} else if (name->len > DNAME_INLINE_LEN-1) {
|
||||||
size_t size = offsetof(struct external_name, name[1]);
|
size_t size = offsetof(struct external_name, name[1]);
|
||||||
struct external_name *p = kmalloc(size + name->len,
|
struct external_name *p = kmalloc(size + name->len,
|
||||||
|
@ -1667,10 +1685,10 @@ static struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
|
||||||
kmem_cache_free(dentry_cache, dentry);
|
kmem_cache_free(dentry_cache, dentry);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
atomic_set(&p->u.count, 1);
|
atomic_set(&p->count, 1);
|
||||||
dname = p->name;
|
dname = p->name;
|
||||||
} else {
|
} else {
|
||||||
dname = dentry->d_iname;
|
dname = dentry->d_shortname.string;
|
||||||
}
|
}
|
||||||
|
|
||||||
dentry->d_name.len = name->len;
|
dentry->d_name.len = name->len;
|
||||||
|
@ -2728,10 +2746,9 @@ static void swap_names(struct dentry *dentry, struct dentry *target)
|
||||||
* dentry:internal, target:external. Steal target's
|
* dentry:internal, target:external. Steal target's
|
||||||
* storage and make target internal.
|
* storage and make target internal.
|
||||||
*/
|
*/
|
||||||
memcpy(target->d_iname, dentry->d_name.name,
|
|
||||||
dentry->d_name.len + 1);
|
|
||||||
dentry->d_name.name = target->d_name.name;
|
dentry->d_name.name = target->d_name.name;
|
||||||
target->d_name.name = target->d_iname;
|
target->d_shortname = dentry->d_shortname;
|
||||||
|
target->d_name.name = target->d_shortname.string;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (unlikely(dname_external(dentry))) {
|
if (unlikely(dname_external(dentry))) {
|
||||||
|
@ -2739,20 +2756,16 @@ static void swap_names(struct dentry *dentry, struct dentry *target)
|
||||||
* dentry:external, target:internal. Give dentry's
|
* dentry:external, target:internal. Give dentry's
|
||||||
* storage to target and make dentry internal
|
* storage to target and make dentry internal
|
||||||
*/
|
*/
|
||||||
memcpy(dentry->d_iname, target->d_name.name,
|
|
||||||
target->d_name.len + 1);
|
|
||||||
target->d_name.name = dentry->d_name.name;
|
target->d_name.name = dentry->d_name.name;
|
||||||
dentry->d_name.name = dentry->d_iname;
|
dentry->d_shortname = target->d_shortname;
|
||||||
|
dentry->d_name.name = dentry->d_shortname.string;
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* Both are internal.
|
* Both are internal.
|
||||||
*/
|
*/
|
||||||
unsigned int i;
|
for (int i = 0; i < DNAME_INLINE_WORDS; i++)
|
||||||
BUILD_BUG_ON(!IS_ALIGNED(DNAME_INLINE_LEN, sizeof(long)));
|
swap(dentry->d_shortname.words[i],
|
||||||
for (i = 0; i < DNAME_INLINE_LEN / sizeof(long); i++) {
|
target->d_shortname.words[i]);
|
||||||
swap(((long *) &dentry->d_iname)[i],
|
|
||||||
((long *) &target->d_iname)[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
swap(dentry->d_name.hash_len, target->d_name.hash_len);
|
swap(dentry->d_name.hash_len, target->d_name.hash_len);
|
||||||
|
@ -2764,16 +2777,15 @@ static void copy_name(struct dentry *dentry, struct dentry *target)
|
||||||
if (unlikely(dname_external(dentry)))
|
if (unlikely(dname_external(dentry)))
|
||||||
old_name = external_name(dentry);
|
old_name = external_name(dentry);
|
||||||
if (unlikely(dname_external(target))) {
|
if (unlikely(dname_external(target))) {
|
||||||
atomic_inc(&external_name(target)->u.count);
|
atomic_inc(&external_name(target)->count);
|
||||||
dentry->d_name = target->d_name;
|
dentry->d_name = target->d_name;
|
||||||
} else {
|
} else {
|
||||||
memcpy(dentry->d_iname, target->d_name.name,
|
dentry->d_shortname = target->d_shortname;
|
||||||
target->d_name.len + 1);
|
dentry->d_name.name = dentry->d_shortname.string;
|
||||||
dentry->d_name.name = dentry->d_iname;
|
|
||||||
dentry->d_name.hash_len = target->d_name.hash_len;
|
dentry->d_name.hash_len = target->d_name.hash_len;
|
||||||
}
|
}
|
||||||
if (old_name && likely(atomic_dec_and_test(&old_name->u.count)))
|
if (old_name && likely(atomic_dec_and_test(&old_name->count)))
|
||||||
kfree_rcu(old_name, u.head);
|
kfree_rcu(old_name, head);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2954,7 +2966,12 @@ static int __d_unalias(struct dentry *dentry, struct dentry *alias)
|
||||||
goto out_err;
|
goto out_err;
|
||||||
m2 = &alias->d_parent->d_inode->i_rwsem;
|
m2 = &alias->d_parent->d_inode->i_rwsem;
|
||||||
out_unalias:
|
out_unalias:
|
||||||
|
if (alias->d_op->d_unalias_trylock &&
|
||||||
|
!alias->d_op->d_unalias_trylock(alias))
|
||||||
|
goto out_err;
|
||||||
__d_move(alias, dentry, false);
|
__d_move(alias, dentry, false);
|
||||||
|
if (alias->d_op->d_unalias_unlock)
|
||||||
|
alias->d_op->d_unalias_unlock(alias);
|
||||||
ret = 0;
|
ret = 0;
|
||||||
out_err:
|
out_err:
|
||||||
if (m2)
|
if (m2)
|
||||||
|
@ -3102,12 +3119,12 @@ void d_mark_tmpfile(struct file *file, struct inode *inode)
|
||||||
{
|
{
|
||||||
struct dentry *dentry = file->f_path.dentry;
|
struct dentry *dentry = file->f_path.dentry;
|
||||||
|
|
||||||
BUG_ON(dentry->d_name.name != dentry->d_iname ||
|
BUG_ON(dname_external(dentry) ||
|
||||||
!hlist_unhashed(&dentry->d_u.d_alias) ||
|
!hlist_unhashed(&dentry->d_u.d_alias) ||
|
||||||
!d_unlinked(dentry));
|
!d_unlinked(dentry));
|
||||||
spin_lock(&dentry->d_parent->d_lock);
|
spin_lock(&dentry->d_parent->d_lock);
|
||||||
spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
|
spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
|
||||||
dentry->d_name.len = sprintf(dentry->d_iname, "#%llu",
|
dentry->d_name.len = sprintf(dentry->d_shortname.string, "#%llu",
|
||||||
(unsigned long long)inode->i_ino);
|
(unsigned long long)inode->i_ino);
|
||||||
spin_unlock(&dentry->d_lock);
|
spin_unlock(&dentry->d_lock);
|
||||||
spin_unlock(&dentry->d_parent->d_lock);
|
spin_unlock(&dentry->d_parent->d_lock);
|
||||||
|
@ -3195,7 +3212,7 @@ static void __init dcache_init(void)
|
||||||
*/
|
*/
|
||||||
dentry_cache = KMEM_CACHE_USERCOPY(dentry,
|
dentry_cache = KMEM_CACHE_USERCOPY(dentry,
|
||||||
SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|SLAB_ACCOUNT,
|
SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|SLAB_ACCOUNT,
|
||||||
d_iname);
|
d_shortname.string);
|
||||||
|
|
||||||
/* Hash may have been set up in dcache_init_early */
|
/* Hash may have been set up in dcache_init_early */
|
||||||
if (!hashdist)
|
if (!hashdist)
|
||||||
|
|
|
@ -17,7 +17,9 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ecryptfs_d_revalidate - revalidate an ecryptfs dentry
|
* ecryptfs_d_revalidate - revalidate an ecryptfs dentry
|
||||||
* @dentry: The ecryptfs dentry
|
* @dir: inode of expected parent
|
||||||
|
* @name: expected name
|
||||||
|
* @dentry: dentry to revalidate
|
||||||
* @flags: lookup flags
|
* @flags: lookup flags
|
||||||
*
|
*
|
||||||
* Called when the VFS needs to revalidate a dentry. This
|
* Called when the VFS needs to revalidate a dentry. This
|
||||||
|
@ -28,7 +30,8 @@
|
||||||
* Returns 1 if valid, 0 otherwise.
|
* Returns 1 if valid, 0 otherwise.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static int ecryptfs_d_revalidate(struct dentry *dentry, unsigned int flags)
|
static int ecryptfs_d_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
|
struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
|
||||||
int rc = 1;
|
int rc = 1;
|
||||||
|
@ -36,8 +39,15 @@ static int ecryptfs_d_revalidate(struct dentry *dentry, unsigned int flags)
|
||||||
if (flags & LOOKUP_RCU)
|
if (flags & LOOKUP_RCU)
|
||||||
return -ECHILD;
|
return -ECHILD;
|
||||||
|
|
||||||
if (lower_dentry->d_flags & DCACHE_OP_REVALIDATE)
|
if (lower_dentry->d_flags & DCACHE_OP_REVALIDATE) {
|
||||||
rc = lower_dentry->d_op->d_revalidate(lower_dentry, flags);
|
struct inode *lower_dir = ecryptfs_inode_to_lower(dir);
|
||||||
|
struct name_snapshot n;
|
||||||
|
|
||||||
|
take_dentry_name_snapshot(&n, lower_dentry);
|
||||||
|
rc = lower_dentry->d_op->d_revalidate(lower_dir, &n.name,
|
||||||
|
lower_dentry, flags);
|
||||||
|
release_dentry_name_snapshot(&n);
|
||||||
|
}
|
||||||
|
|
||||||
if (d_really_is_positive(dentry)) {
|
if (d_really_is_positive(dentry)) {
|
||||||
struct inode *inode = d_inode(dentry);
|
struct inode *inode = d_inode(dentry);
|
||||||
|
|
|
@ -31,10 +31,9 @@ static inline void exfat_d_version_set(struct dentry *dentry,
|
||||||
* If it happened, the negative dentry isn't actually negative anymore. So,
|
* If it happened, the negative dentry isn't actually negative anymore. So,
|
||||||
* drop it.
|
* drop it.
|
||||||
*/
|
*/
|
||||||
static int exfat_d_revalidate(struct dentry *dentry, unsigned int flags)
|
static int exfat_d_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (flags & LOOKUP_RCU)
|
if (flags & LOOKUP_RCU)
|
||||||
return -ECHILD;
|
return -ECHILD;
|
||||||
|
|
||||||
|
@ -58,11 +57,7 @@ static int exfat_d_revalidate(struct dentry *dentry, unsigned int flags)
|
||||||
if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET))
|
if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
spin_lock(&dentry->d_lock);
|
return inode_eq_iversion(dir, exfat_d_version(dentry));
|
||||||
ret = inode_eq_iversion(d_inode(dentry->d_parent),
|
|
||||||
exfat_d_version(dentry));
|
|
||||||
spin_unlock(&dentry->d_lock);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* returns the length of a struct qstr, ignoring trailing dots if necessary */
|
/* returns the length of a struct qstr, ignoring trailing dots if necessary */
|
||||||
|
|
|
@ -322,9 +322,7 @@ restart:
|
||||||
WARN_ON(!list_empty(&ei->i_fc_dilist));
|
WARN_ON(!list_empty(&ei->i_fc_dilist));
|
||||||
spin_unlock(&sbi->s_fc_lock);
|
spin_unlock(&sbi->s_fc_lock);
|
||||||
|
|
||||||
if (fc_dentry->fcd_name.name &&
|
release_dentry_name_snapshot(&fc_dentry->fcd_name);
|
||||||
fc_dentry->fcd_name.len > DNAME_INLINE_LEN)
|
|
||||||
kfree(fc_dentry->fcd_name.name);
|
|
||||||
kmem_cache_free(ext4_fc_dentry_cachep, fc_dentry);
|
kmem_cache_free(ext4_fc_dentry_cachep, fc_dentry);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -449,22 +447,7 @@ static int __track_dentry_update(handle_t *handle, struct inode *inode,
|
||||||
node->fcd_op = dentry_update->op;
|
node->fcd_op = dentry_update->op;
|
||||||
node->fcd_parent = dir->i_ino;
|
node->fcd_parent = dir->i_ino;
|
||||||
node->fcd_ino = inode->i_ino;
|
node->fcd_ino = inode->i_ino;
|
||||||
if (dentry->d_name.len > DNAME_INLINE_LEN) {
|
take_dentry_name_snapshot(&node->fcd_name, dentry);
|
||||||
node->fcd_name.name = kmalloc(dentry->d_name.len, GFP_NOFS);
|
|
||||||
if (!node->fcd_name.name) {
|
|
||||||
kmem_cache_free(ext4_fc_dentry_cachep, node);
|
|
||||||
ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_NOMEM, handle);
|
|
||||||
mutex_lock(&ei->i_fc_lock);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
memcpy((u8 *)node->fcd_name.name, dentry->d_name.name,
|
|
||||||
dentry->d_name.len);
|
|
||||||
} else {
|
|
||||||
memcpy(node->fcd_iname, dentry->d_name.name,
|
|
||||||
dentry->d_name.len);
|
|
||||||
node->fcd_name.name = node->fcd_iname;
|
|
||||||
}
|
|
||||||
node->fcd_name.len = dentry->d_name.len;
|
|
||||||
INIT_LIST_HEAD(&node->fcd_dilist);
|
INIT_LIST_HEAD(&node->fcd_dilist);
|
||||||
spin_lock(&sbi->s_fc_lock);
|
spin_lock(&sbi->s_fc_lock);
|
||||||
if (sbi->s_journal->j_flags & JBD2_FULL_COMMIT_ONGOING ||
|
if (sbi->s_journal->j_flags & JBD2_FULL_COMMIT_ONGOING ||
|
||||||
|
@ -832,7 +815,7 @@ static bool ext4_fc_add_dentry_tlv(struct super_block *sb, u32 *crc,
|
||||||
{
|
{
|
||||||
struct ext4_fc_dentry_info fcd;
|
struct ext4_fc_dentry_info fcd;
|
||||||
struct ext4_fc_tl tl;
|
struct ext4_fc_tl tl;
|
||||||
int dlen = fc_dentry->fcd_name.len;
|
int dlen = fc_dentry->fcd_name.name.len;
|
||||||
u8 *dst = ext4_fc_reserve_space(sb,
|
u8 *dst = ext4_fc_reserve_space(sb,
|
||||||
EXT4_FC_TAG_BASE_LEN + sizeof(fcd) + dlen, crc);
|
EXT4_FC_TAG_BASE_LEN + sizeof(fcd) + dlen, crc);
|
||||||
|
|
||||||
|
@ -847,7 +830,7 @@ static bool ext4_fc_add_dentry_tlv(struct super_block *sb, u32 *crc,
|
||||||
dst += EXT4_FC_TAG_BASE_LEN;
|
dst += EXT4_FC_TAG_BASE_LEN;
|
||||||
memcpy(dst, &fcd, sizeof(fcd));
|
memcpy(dst, &fcd, sizeof(fcd));
|
||||||
dst += sizeof(fcd);
|
dst += sizeof(fcd);
|
||||||
memcpy(dst, fc_dentry->fcd_name.name, dlen);
|
memcpy(dst, fc_dentry->fcd_name.name.name, dlen);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1328,9 +1311,7 @@ static void ext4_fc_cleanup(journal_t *journal, int full, tid_t tid)
|
||||||
list_del_init(&fc_dentry->fcd_dilist);
|
list_del_init(&fc_dentry->fcd_dilist);
|
||||||
spin_unlock(&sbi->s_fc_lock);
|
spin_unlock(&sbi->s_fc_lock);
|
||||||
|
|
||||||
if (fc_dentry->fcd_name.name &&
|
release_dentry_name_snapshot(&fc_dentry->fcd_name);
|
||||||
fc_dentry->fcd_name.len > DNAME_INLINE_LEN)
|
|
||||||
kfree(fc_dentry->fcd_name.name);
|
|
||||||
kmem_cache_free(ext4_fc_dentry_cachep, fc_dentry);
|
kmem_cache_free(ext4_fc_dentry_cachep, fc_dentry);
|
||||||
spin_lock(&sbi->s_fc_lock);
|
spin_lock(&sbi->s_fc_lock);
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,8 +109,7 @@ struct ext4_fc_dentry_update {
|
||||||
int fcd_op; /* Type of update create / unlink / link */
|
int fcd_op; /* Type of update create / unlink / link */
|
||||||
int fcd_parent; /* Parent inode number */
|
int fcd_parent; /* Parent inode number */
|
||||||
int fcd_ino; /* Inode number */
|
int fcd_ino; /* Inode number */
|
||||||
struct qstr fcd_name; /* Dirent name */
|
struct name_snapshot fcd_name; /* Dirent name */
|
||||||
unsigned char fcd_iname[DNAME_INLINE_LEN]; /* Dirent name string */
|
|
||||||
struct list_head fcd_list;
|
struct list_head fcd_list;
|
||||||
struct list_head fcd_dilist;
|
struct list_head fcd_dilist;
|
||||||
};
|
};
|
||||||
|
|
|
@ -43,17 +43,13 @@ static inline void vfat_d_version_set(struct dentry *dentry,
|
||||||
* If it happened, the negative dentry isn't actually negative
|
* If it happened, the negative dentry isn't actually negative
|
||||||
* anymore. So, drop it.
|
* anymore. So, drop it.
|
||||||
*/
|
*/
|
||||||
static int vfat_revalidate_shortname(struct dentry *dentry)
|
static bool vfat_revalidate_shortname(struct dentry *dentry, struct inode *dir)
|
||||||
{
|
{
|
||||||
int ret = 1;
|
return inode_eq_iversion(dir, vfat_d_version(dentry));
|
||||||
spin_lock(&dentry->d_lock);
|
|
||||||
if (!inode_eq_iversion(d_inode(dentry->d_parent), vfat_d_version(dentry)))
|
|
||||||
ret = 0;
|
|
||||||
spin_unlock(&dentry->d_lock);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vfat_revalidate(struct dentry *dentry, unsigned int flags)
|
static int vfat_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
if (flags & LOOKUP_RCU)
|
if (flags & LOOKUP_RCU)
|
||||||
return -ECHILD;
|
return -ECHILD;
|
||||||
|
@ -61,10 +57,11 @@ static int vfat_revalidate(struct dentry *dentry, unsigned int flags)
|
||||||
/* This is not negative dentry. Always valid. */
|
/* This is not negative dentry. Always valid. */
|
||||||
if (d_really_is_positive(dentry))
|
if (d_really_is_positive(dentry))
|
||||||
return 1;
|
return 1;
|
||||||
return vfat_revalidate_shortname(dentry);
|
return vfat_revalidate_shortname(dentry, dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vfat_revalidate_ci(struct dentry *dentry, unsigned int flags)
|
static int vfat_revalidate_ci(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
if (flags & LOOKUP_RCU)
|
if (flags & LOOKUP_RCU)
|
||||||
return -ECHILD;
|
return -ECHILD;
|
||||||
|
@ -97,7 +94,7 @@ static int vfat_revalidate_ci(struct dentry *dentry, unsigned int flags)
|
||||||
if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET))
|
if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return vfat_revalidate_shortname(dentry);
|
return vfat_revalidate_shortname(dentry, dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* returns the length of a struct qstr, ignoring trailing dots */
|
/* returns the length of a struct qstr, ignoring trailing dots */
|
||||||
|
|
|
@ -175,10 +175,12 @@ static void fuse_lookup_init(struct fuse_conn *fc, struct fuse_args *args,
|
||||||
memset(outarg, 0, sizeof(struct fuse_entry_out));
|
memset(outarg, 0, sizeof(struct fuse_entry_out));
|
||||||
args->opcode = FUSE_LOOKUP;
|
args->opcode = FUSE_LOOKUP;
|
||||||
args->nodeid = nodeid;
|
args->nodeid = nodeid;
|
||||||
args->in_numargs = 2;
|
args->in_numargs = 3;
|
||||||
fuse_set_zero_arg0(args);
|
fuse_set_zero_arg0(args);
|
||||||
args->in_args[1].size = name->len + 1;
|
args->in_args[1].size = name->len;
|
||||||
args->in_args[1].value = name->name;
|
args->in_args[1].value = name->name;
|
||||||
|
args->in_args[2].size = 1;
|
||||||
|
args->in_args[2].value = "";
|
||||||
args->out_numargs = 1;
|
args->out_numargs = 1;
|
||||||
args->out_args[0].size = sizeof(struct fuse_entry_out);
|
args->out_args[0].size = sizeof(struct fuse_entry_out);
|
||||||
args->out_args[0].value = outarg;
|
args->out_args[0].value = outarg;
|
||||||
|
@ -193,10 +195,10 @@ static void fuse_lookup_init(struct fuse_conn *fc, struct fuse_args *args,
|
||||||
* the lookup once more. If the lookup results in the same inode,
|
* the lookup once more. If the lookup results in the same inode,
|
||||||
* then refresh the attributes, timeouts and mark the dentry valid.
|
* then refresh the attributes, timeouts and mark the dentry valid.
|
||||||
*/
|
*/
|
||||||
static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
|
static int fuse_dentry_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *entry, unsigned int flags)
|
||||||
{
|
{
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
struct dentry *parent;
|
|
||||||
struct fuse_mount *fm;
|
struct fuse_mount *fm;
|
||||||
struct fuse_inode *fi;
|
struct fuse_inode *fi;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -228,11 +230,9 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
|
||||||
|
|
||||||
attr_version = fuse_get_attr_version(fm->fc);
|
attr_version = fuse_get_attr_version(fm->fc);
|
||||||
|
|
||||||
parent = dget_parent(entry);
|
fuse_lookup_init(fm->fc, &args, get_node_id(dir),
|
||||||
fuse_lookup_init(fm->fc, &args, get_node_id(d_inode(parent)),
|
name, &outarg);
|
||||||
&entry->d_name, &outarg);
|
|
||||||
ret = fuse_simple_request(fm, &args);
|
ret = fuse_simple_request(fm, &args);
|
||||||
dput(parent);
|
|
||||||
/* Zero nodeid is same as -ENOENT */
|
/* Zero nodeid is same as -ENOENT */
|
||||||
if (!ret && !outarg.nodeid)
|
if (!ret && !outarg.nodeid)
|
||||||
ret = -ENOENT;
|
ret = -ENOENT;
|
||||||
|
@ -266,9 +266,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
|
||||||
if (test_bit(FUSE_I_INIT_RDPLUS, &fi->state))
|
if (test_bit(FUSE_I_INIT_RDPLUS, &fi->state))
|
||||||
return -ECHILD;
|
return -ECHILD;
|
||||||
} else if (test_and_clear_bit(FUSE_I_INIT_RDPLUS, &fi->state)) {
|
} else if (test_and_clear_bit(FUSE_I_INIT_RDPLUS, &fi->state)) {
|
||||||
parent = dget_parent(entry);
|
fuse_advise_use_readdirplus(dir);
|
||||||
fuse_advise_use_readdirplus(d_inode(parent));
|
|
||||||
dput(parent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret = 1;
|
ret = 1;
|
||||||
|
|
|
@ -21,7 +21,9 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gfs2_drevalidate - Check directory lookup consistency
|
* gfs2_drevalidate - Check directory lookup consistency
|
||||||
* @dentry: the mapping to check
|
* @dir: expected parent directory inode
|
||||||
|
* @name: expexted name
|
||||||
|
* @dentry: dentry to check
|
||||||
* @flags: lookup flags
|
* @flags: lookup flags
|
||||||
*
|
*
|
||||||
* Check to make sure the lookup necessary to arrive at this inode from its
|
* Check to make sure the lookup necessary to arrive at this inode from its
|
||||||
|
@ -30,50 +32,43 @@
|
||||||
* Returns: 1 if the dentry is ok, 0 if it isn't
|
* Returns: 1 if the dentry is ok, 0 if it isn't
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int gfs2_drevalidate(struct dentry *dentry, unsigned int flags)
|
static int gfs2_drevalidate(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
struct dentry *parent;
|
struct gfs2_sbd *sdp = GFS2_SB(dir);
|
||||||
struct gfs2_sbd *sdp;
|
struct gfs2_inode *dip = GFS2_I(dir);
|
||||||
struct gfs2_inode *dip;
|
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
struct gfs2_holder d_gh;
|
struct gfs2_holder d_gh;
|
||||||
struct gfs2_inode *ip = NULL;
|
struct gfs2_inode *ip = NULL;
|
||||||
int error, valid = 0;
|
int error, valid;
|
||||||
int had_lock = 0;
|
int had_lock = 0;
|
||||||
|
|
||||||
if (flags & LOOKUP_RCU)
|
if (flags & LOOKUP_RCU)
|
||||||
return -ECHILD;
|
return -ECHILD;
|
||||||
|
|
||||||
parent = dget_parent(dentry);
|
|
||||||
sdp = GFS2_SB(d_inode(parent));
|
|
||||||
dip = GFS2_I(d_inode(parent));
|
|
||||||
inode = d_inode(dentry);
|
inode = d_inode(dentry);
|
||||||
|
|
||||||
if (inode) {
|
if (inode) {
|
||||||
if (is_bad_inode(inode))
|
if (is_bad_inode(inode))
|
||||||
goto out;
|
return 0;
|
||||||
ip = GFS2_I(inode);
|
ip = GFS2_I(inode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sdp->sd_lockstruct.ls_ops->lm_mount == NULL) {
|
if (sdp->sd_lockstruct.ls_ops->lm_mount == NULL)
|
||||||
valid = 1;
|
return 1;
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
had_lock = (gfs2_glock_is_locked_by_me(dip->i_gl) != NULL);
|
had_lock = (gfs2_glock_is_locked_by_me(dip->i_gl) != NULL);
|
||||||
if (!had_lock) {
|
if (!had_lock) {
|
||||||
error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &d_gh);
|
error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &d_gh);
|
||||||
if (error)
|
if (error)
|
||||||
goto out;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = gfs2_dir_check(d_inode(parent), &dentry->d_name, ip);
|
error = gfs2_dir_check(dir, name, ip);
|
||||||
valid = inode ? !error : (error == -ENOENT);
|
valid = inode ? !error : (error == -ENOENT);
|
||||||
|
|
||||||
if (!had_lock)
|
if (!had_lock)
|
||||||
gfs2_glock_dq_uninit(&d_gh);
|
gfs2_glock_dq_uninit(&d_gh);
|
||||||
out:
|
|
||||||
dput(parent);
|
|
||||||
return valid;
|
return valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,8 @@
|
||||||
|
|
||||||
/* dentry case-handling: just lowercase everything */
|
/* dentry case-handling: just lowercase everything */
|
||||||
|
|
||||||
static int hfs_revalidate_dentry(struct dentry *dentry, unsigned int flags)
|
static int hfs_revalidate_dentry(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
int diff;
|
int diff;
|
||||||
|
|
|
@ -1576,7 +1576,8 @@ out:
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int jfs_ci_revalidate(struct dentry *dentry, unsigned int flags)
|
static int jfs_ci_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* This is not negative dentry. Always valid.
|
* This is not negative dentry. Always valid.
|
||||||
|
|
|
@ -1109,7 +1109,8 @@ struct kernfs_node *kernfs_create_empty_dir(struct kernfs_node *parent,
|
||||||
return ERR_PTR(rc);
|
return ERR_PTR(rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags)
|
static int kernfs_dop_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
struct kernfs_node *kn;
|
struct kernfs_node *kn;
|
||||||
struct kernfs_root *root;
|
struct kernfs_root *root;
|
||||||
|
|
15
fs/libfs.c
15
fs/libfs.c
|
@ -1782,7 +1782,7 @@ int generic_ci_d_compare(const struct dentry *dentry, unsigned int len,
|
||||||
{
|
{
|
||||||
const struct dentry *parent;
|
const struct dentry *parent;
|
||||||
const struct inode *dir;
|
const struct inode *dir;
|
||||||
char strbuf[DNAME_INLINE_LEN];
|
union shortname_store strbuf;
|
||||||
struct qstr qstr;
|
struct qstr qstr;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1802,22 +1802,23 @@ int generic_ci_d_compare(const struct dentry *dentry, unsigned int len,
|
||||||
if (!dir || !IS_CASEFOLDED(dir))
|
if (!dir || !IS_CASEFOLDED(dir))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
qstr.len = len;
|
||||||
|
qstr.name = str;
|
||||||
/*
|
/*
|
||||||
* If the dentry name is stored in-line, then it may be concurrently
|
* If the dentry name is stored in-line, then it may be concurrently
|
||||||
* modified by a rename. If this happens, the VFS will eventually retry
|
* modified by a rename. If this happens, the VFS will eventually retry
|
||||||
* the lookup, so it doesn't matter what ->d_compare() returns.
|
* the lookup, so it doesn't matter what ->d_compare() returns.
|
||||||
* However, it's unsafe to call utf8_strncasecmp() with an unstable
|
* However, it's unsafe to call utf8_strncasecmp() with an unstable
|
||||||
* string. Therefore, we have to copy the name into a temporary buffer.
|
* string. Therefore, we have to copy the name into a temporary buffer.
|
||||||
|
* As above, len is guaranteed to match str, so the shortname case
|
||||||
|
* is exactly when str points to ->d_shortname.
|
||||||
*/
|
*/
|
||||||
if (len <= DNAME_INLINE_LEN - 1) {
|
if (qstr.name == dentry->d_shortname.string) {
|
||||||
memcpy(strbuf, str, len);
|
strbuf = dentry->d_shortname; // NUL is guaranteed to be in there
|
||||||
strbuf[len] = 0;
|
qstr.name = strbuf.string;
|
||||||
str = strbuf;
|
|
||||||
/* prevent compiler from optimizing out the temporary buffer */
|
/* prevent compiler from optimizing out the temporary buffer */
|
||||||
barrier();
|
barrier();
|
||||||
}
|
}
|
||||||
qstr.len = len;
|
|
||||||
qstr.name = str;
|
|
||||||
|
|
||||||
return utf8_strncasecmp(dentry->d_sb->s_encoding, name, &qstr);
|
return utf8_strncasecmp(dentry->d_sb->s_encoding, name, &qstr);
|
||||||
}
|
}
|
||||||
|
|
18
fs/namei.c
18
fs/namei.c
|
@ -921,10 +921,11 @@ out_dput:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int d_revalidate(struct dentry *dentry, unsigned int flags)
|
static inline int d_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE))
|
if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE))
|
||||||
return dentry->d_op->d_revalidate(dentry, flags);
|
return dentry->d_op->d_revalidate(dir, name, dentry, flags);
|
||||||
else
|
else
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -1652,7 +1653,7 @@ static struct dentry *lookup_dcache(const struct qstr *name,
|
||||||
{
|
{
|
||||||
struct dentry *dentry = d_lookup(dir, name);
|
struct dentry *dentry = d_lookup(dir, name);
|
||||||
if (dentry) {
|
if (dentry) {
|
||||||
int error = d_revalidate(dentry, flags);
|
int error = d_revalidate(dir->d_inode, name, dentry, flags);
|
||||||
if (unlikely(error <= 0)) {
|
if (unlikely(error <= 0)) {
|
||||||
if (!error)
|
if (!error)
|
||||||
d_invalidate(dentry);
|
d_invalidate(dentry);
|
||||||
|
@ -1737,19 +1738,20 @@ static struct dentry *lookup_fast(struct nameidata *nd)
|
||||||
if (read_seqcount_retry(&parent->d_seq, nd->seq))
|
if (read_seqcount_retry(&parent->d_seq, nd->seq))
|
||||||
return ERR_PTR(-ECHILD);
|
return ERR_PTR(-ECHILD);
|
||||||
|
|
||||||
status = d_revalidate(dentry, nd->flags);
|
status = d_revalidate(nd->inode, &nd->last, dentry, nd->flags);
|
||||||
if (likely(status > 0))
|
if (likely(status > 0))
|
||||||
return dentry;
|
return dentry;
|
||||||
if (!try_to_unlazy_next(nd, dentry))
|
if (!try_to_unlazy_next(nd, dentry))
|
||||||
return ERR_PTR(-ECHILD);
|
return ERR_PTR(-ECHILD);
|
||||||
if (status == -ECHILD)
|
if (status == -ECHILD)
|
||||||
/* we'd been told to redo it in non-rcu mode */
|
/* we'd been told to redo it in non-rcu mode */
|
||||||
status = d_revalidate(dentry, nd->flags);
|
status = d_revalidate(nd->inode, &nd->last,
|
||||||
|
dentry, nd->flags);
|
||||||
} else {
|
} else {
|
||||||
dentry = __d_lookup(parent, &nd->last);
|
dentry = __d_lookup(parent, &nd->last);
|
||||||
if (unlikely(!dentry))
|
if (unlikely(!dentry))
|
||||||
return NULL;
|
return NULL;
|
||||||
status = d_revalidate(dentry, nd->flags);
|
status = d_revalidate(nd->inode, &nd->last, dentry, nd->flags);
|
||||||
}
|
}
|
||||||
if (unlikely(status <= 0)) {
|
if (unlikely(status <= 0)) {
|
||||||
if (!status)
|
if (!status)
|
||||||
|
@ -1777,7 +1779,7 @@ again:
|
||||||
if (IS_ERR(dentry))
|
if (IS_ERR(dentry))
|
||||||
return dentry;
|
return dentry;
|
||||||
if (unlikely(!d_in_lookup(dentry))) {
|
if (unlikely(!d_in_lookup(dentry))) {
|
||||||
int error = d_revalidate(dentry, flags);
|
int error = d_revalidate(inode, name, dentry, flags);
|
||||||
if (unlikely(error <= 0)) {
|
if (unlikely(error <= 0)) {
|
||||||
if (!error) {
|
if (!error) {
|
||||||
d_invalidate(dentry);
|
d_invalidate(dentry);
|
||||||
|
@ -3575,7 +3577,7 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
|
||||||
if (d_in_lookup(dentry))
|
if (d_in_lookup(dentry))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
error = d_revalidate(dentry, nd->flags);
|
error = d_revalidate(dir_inode, &nd->last, dentry, nd->flags);
|
||||||
if (likely(error > 0))
|
if (likely(error > 0))
|
||||||
break;
|
break;
|
||||||
if (error)
|
if (error)
|
||||||
|
|
62
fs/nfs/dir.c
62
fs/nfs/dir.c
|
@ -1672,7 +1672,7 @@ nfs_lookup_revalidate_delegated(struct inode *dir, struct dentry *dentry,
|
||||||
return nfs_lookup_revalidate_done(dir, dentry, inode, 1);
|
return nfs_lookup_revalidate_done(dir, dentry, inode, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nfs_lookup_revalidate_dentry(struct inode *dir,
|
static int nfs_lookup_revalidate_dentry(struct inode *dir, const struct qstr *name,
|
||||||
struct dentry *dentry,
|
struct dentry *dentry,
|
||||||
struct inode *inode, unsigned int flags)
|
struct inode *inode, unsigned int flags)
|
||||||
{
|
{
|
||||||
|
@ -1690,7 +1690,7 @@ static int nfs_lookup_revalidate_dentry(struct inode *dir,
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
dir_verifier = nfs_save_change_attribute(dir);
|
dir_verifier = nfs_save_change_attribute(dir);
|
||||||
ret = NFS_PROTO(dir)->lookup(dir, dentry, fhandle, fattr);
|
ret = NFS_PROTO(dir)->lookup(dir, dentry, name, fhandle, fattr);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -1732,8 +1732,8 @@ out:
|
||||||
* cached dentry and do a new lookup.
|
* cached dentry and do a new lookup.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
nfs_do_lookup_revalidate(struct inode *dir, struct dentry *dentry,
|
nfs_do_lookup_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
unsigned int flags)
|
struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
@ -1775,7 +1775,7 @@ nfs_do_lookup_revalidate(struct inode *dir, struct dentry *dentry,
|
||||||
if (NFS_STALE(inode))
|
if (NFS_STALE(inode))
|
||||||
goto out_bad;
|
goto out_bad;
|
||||||
|
|
||||||
return nfs_lookup_revalidate_dentry(dir, dentry, inode, flags);
|
return nfs_lookup_revalidate_dentry(dir, name, dentry, inode, flags);
|
||||||
out_valid:
|
out_valid:
|
||||||
return nfs_lookup_revalidate_done(dir, dentry, inode, 1);
|
return nfs_lookup_revalidate_done(dir, dentry, inode, 1);
|
||||||
out_bad:
|
out_bad:
|
||||||
|
@ -1785,38 +1785,26 @@ out_bad:
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
__nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags,
|
__nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
|
||||||
int (*reval)(struct inode *, struct dentry *, unsigned int))
|
|
||||||
{
|
{
|
||||||
struct dentry *parent;
|
|
||||||
struct inode *dir;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (flags & LOOKUP_RCU) {
|
if (flags & LOOKUP_RCU) {
|
||||||
if (dentry->d_fsdata == NFS_FSDATA_BLOCKED)
|
if (dentry->d_fsdata == NFS_FSDATA_BLOCKED)
|
||||||
return -ECHILD;
|
return -ECHILD;
|
||||||
parent = READ_ONCE(dentry->d_parent);
|
|
||||||
dir = d_inode_rcu(parent);
|
|
||||||
if (!dir)
|
|
||||||
return -ECHILD;
|
|
||||||
ret = reval(dir, dentry, flags);
|
|
||||||
if (parent != READ_ONCE(dentry->d_parent))
|
|
||||||
return -ECHILD;
|
|
||||||
} else {
|
} else {
|
||||||
/* Wait for unlink to complete - see unblock_revalidate() */
|
/* Wait for unlink to complete - see unblock_revalidate() */
|
||||||
wait_var_event(&dentry->d_fsdata,
|
wait_var_event(&dentry->d_fsdata,
|
||||||
smp_load_acquire(&dentry->d_fsdata)
|
smp_load_acquire(&dentry->d_fsdata)
|
||||||
!= NFS_FSDATA_BLOCKED);
|
!= NFS_FSDATA_BLOCKED);
|
||||||
parent = dget_parent(dentry);
|
|
||||||
ret = reval(d_inode(parent), dentry, flags);
|
|
||||||
dput(parent);
|
|
||||||
}
|
}
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
|
static int nfs_lookup_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
return __nfs_lookup_revalidate(dentry, flags, nfs_do_lookup_revalidate);
|
if (__nfs_lookup_revalidate(dentry, flags))
|
||||||
|
return -ECHILD;
|
||||||
|
return nfs_do_lookup_revalidate(dir, name, dentry, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void block_revalidate(struct dentry *dentry)
|
static void block_revalidate(struct dentry *dentry)
|
||||||
|
@ -1982,7 +1970,8 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in
|
||||||
|
|
||||||
dir_verifier = nfs_save_change_attribute(dir);
|
dir_verifier = nfs_save_change_attribute(dir);
|
||||||
trace_nfs_lookup_enter(dir, dentry, flags);
|
trace_nfs_lookup_enter(dir, dentry, flags);
|
||||||
error = NFS_PROTO(dir)->lookup(dir, dentry, fhandle, fattr);
|
error = NFS_PROTO(dir)->lookup(dir, dentry, &dentry->d_name,
|
||||||
|
fhandle, fattr);
|
||||||
if (error == -ENOENT) {
|
if (error == -ENOENT) {
|
||||||
if (nfs_server_capable(dir, NFS_CAP_CASE_INSENSITIVE))
|
if (nfs_server_capable(dir, NFS_CAP_CASE_INSENSITIVE))
|
||||||
dir_verifier = inode_peek_iversion_raw(dir);
|
dir_verifier = inode_peek_iversion_raw(dir);
|
||||||
|
@ -2025,7 +2014,8 @@ void nfs_d_prune_case_insensitive_aliases(struct inode *inode)
|
||||||
EXPORT_SYMBOL_GPL(nfs_d_prune_case_insensitive_aliases);
|
EXPORT_SYMBOL_GPL(nfs_d_prune_case_insensitive_aliases);
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_NFS_V4)
|
#if IS_ENABLED(CONFIG_NFS_V4)
|
||||||
static int nfs4_lookup_revalidate(struct dentry *, unsigned int);
|
static int nfs4_lookup_revalidate(struct inode *, const struct qstr *,
|
||||||
|
struct dentry *, unsigned int);
|
||||||
|
|
||||||
const struct dentry_operations nfs4_dentry_operations = {
|
const struct dentry_operations nfs4_dentry_operations = {
|
||||||
.d_revalidate = nfs4_lookup_revalidate,
|
.d_revalidate = nfs4_lookup_revalidate,
|
||||||
|
@ -2214,11 +2204,14 @@ no_open:
|
||||||
EXPORT_SYMBOL_GPL(nfs_atomic_open);
|
EXPORT_SYMBOL_GPL(nfs_atomic_open);
|
||||||
|
|
||||||
static int
|
static int
|
||||||
nfs4_do_lookup_revalidate(struct inode *dir, struct dentry *dentry,
|
nfs4_lookup_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
unsigned int flags)
|
struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
|
|
||||||
|
if (__nfs_lookup_revalidate(dentry, flags))
|
||||||
|
return -ECHILD;
|
||||||
|
|
||||||
trace_nfs_lookup_revalidate_enter(dir, dentry, flags);
|
trace_nfs_lookup_revalidate_enter(dir, dentry, flags);
|
||||||
|
|
||||||
if (!(flags & LOOKUP_OPEN) || (flags & LOOKUP_DIRECTORY))
|
if (!(flags & LOOKUP_OPEN) || (flags & LOOKUP_DIRECTORY))
|
||||||
|
@ -2254,16 +2247,10 @@ nfs4_do_lookup_revalidate(struct inode *dir, struct dentry *dentry,
|
||||||
reval_dentry:
|
reval_dentry:
|
||||||
if (flags & LOOKUP_RCU)
|
if (flags & LOOKUP_RCU)
|
||||||
return -ECHILD;
|
return -ECHILD;
|
||||||
return nfs_lookup_revalidate_dentry(dir, dentry, inode, flags);
|
return nfs_lookup_revalidate_dentry(dir, name, dentry, inode, flags);
|
||||||
|
|
||||||
full_reval:
|
full_reval:
|
||||||
return nfs_do_lookup_revalidate(dir, dentry, flags);
|
return nfs_do_lookup_revalidate(dir, name, dentry, flags);
|
||||||
}
|
|
||||||
|
|
||||||
static int nfs4_lookup_revalidate(struct dentry *dentry, unsigned int flags)
|
|
||||||
{
|
|
||||||
return __nfs_lookup_revalidate(dentry, flags,
|
|
||||||
nfs4_do_lookup_revalidate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_NFSV4 */
|
#endif /* CONFIG_NFSV4 */
|
||||||
|
@ -2319,7 +2306,8 @@ nfs_add_or_obtain(struct dentry *dentry, struct nfs_fh *fhandle,
|
||||||
d_drop(dentry);
|
d_drop(dentry);
|
||||||
|
|
||||||
if (fhandle->size == 0) {
|
if (fhandle->size == 0) {
|
||||||
error = NFS_PROTO(dir)->lookup(dir, dentry, fhandle, fattr);
|
error = NFS_PROTO(dir)->lookup(dir, dentry, &dentry->d_name,
|
||||||
|
fhandle, fattr);
|
||||||
if (error)
|
if (error)
|
||||||
goto out_error;
|
goto out_error;
|
||||||
}
|
}
|
||||||
|
|
|
@ -308,7 +308,7 @@ int nfs_submount(struct fs_context *fc, struct nfs_server *server)
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
/* Look it up again to get its attributes */
|
/* Look it up again to get its attributes */
|
||||||
err = server->nfs_client->rpc_ops->lookup(d_inode(parent), dentry,
|
err = server->nfs_client->rpc_ops->lookup(d_inode(parent), dentry, &dentry->d_name,
|
||||||
ctx->mntfh, ctx->clone_data.fattr);
|
ctx->mntfh, ctx->clone_data.fattr);
|
||||||
dput(parent);
|
dput(parent);
|
||||||
if (err != 0)
|
if (err != 0)
|
||||||
|
|
|
@ -192,7 +192,7 @@ __nfs3_proc_lookup(struct inode *dir, const char *name, size_t len,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
nfs3_proc_lookup(struct inode *dir, struct dentry *dentry,
|
nfs3_proc_lookup(struct inode *dir, struct dentry *dentry, const struct qstr *name,
|
||||||
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
||||||
{
|
{
|
||||||
unsigned short task_flags = 0;
|
unsigned short task_flags = 0;
|
||||||
|
@ -202,8 +202,7 @@ nfs3_proc_lookup(struct inode *dir, struct dentry *dentry,
|
||||||
task_flags |= RPC_TASK_TIMEOUT;
|
task_flags |= RPC_TASK_TIMEOUT;
|
||||||
|
|
||||||
dprintk("NFS call lookup %pd2\n", dentry);
|
dprintk("NFS call lookup %pd2\n", dentry);
|
||||||
return __nfs3_proc_lookup(dir, dentry->d_name.name,
|
return __nfs3_proc_lookup(dir, name->name, name->len, fhandle, fattr,
|
||||||
dentry->d_name.len, fhandle, fattr,
|
|
||||||
task_flags);
|
task_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4544,15 +4544,15 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir,
|
static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir,
|
||||||
struct dentry *dentry, struct nfs_fh *fhandle,
|
struct dentry *dentry, const struct qstr *name,
|
||||||
struct nfs_fattr *fattr)
|
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
||||||
{
|
{
|
||||||
struct nfs_server *server = NFS_SERVER(dir);
|
struct nfs_server *server = NFS_SERVER(dir);
|
||||||
int status;
|
int status;
|
||||||
struct nfs4_lookup_arg args = {
|
struct nfs4_lookup_arg args = {
|
||||||
.bitmask = server->attr_bitmask,
|
.bitmask = server->attr_bitmask,
|
||||||
.dir_fh = NFS_FH(dir),
|
.dir_fh = NFS_FH(dir),
|
||||||
.name = &dentry->d_name,
|
.name = name,
|
||||||
};
|
};
|
||||||
struct nfs4_lookup_res res = {
|
struct nfs4_lookup_res res = {
|
||||||
.server = server,
|
.server = server,
|
||||||
|
@ -4594,17 +4594,16 @@ static void nfs_fixup_secinfo_attributes(struct nfs_fattr *fattr)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nfs4_proc_lookup_common(struct rpc_clnt **clnt, struct inode *dir,
|
static int nfs4_proc_lookup_common(struct rpc_clnt **clnt, struct inode *dir,
|
||||||
struct dentry *dentry, struct nfs_fh *fhandle,
|
struct dentry *dentry, const struct qstr *name,
|
||||||
struct nfs_fattr *fattr)
|
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
||||||
{
|
{
|
||||||
struct nfs4_exception exception = {
|
struct nfs4_exception exception = {
|
||||||
.interruptible = true,
|
.interruptible = true,
|
||||||
};
|
};
|
||||||
struct rpc_clnt *client = *clnt;
|
struct rpc_clnt *client = *clnt;
|
||||||
const struct qstr *name = &dentry->d_name;
|
|
||||||
int err;
|
int err;
|
||||||
do {
|
do {
|
||||||
err = _nfs4_proc_lookup(client, dir, dentry, fhandle, fattr);
|
err = _nfs4_proc_lookup(client, dir, dentry, name, fhandle, fattr);
|
||||||
trace_nfs4_lookup(dir, name, err);
|
trace_nfs4_lookup(dir, name, err);
|
||||||
switch (err) {
|
switch (err) {
|
||||||
case -NFS4ERR_BADNAME:
|
case -NFS4ERR_BADNAME:
|
||||||
|
@ -4639,13 +4638,13 @@ out:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nfs4_proc_lookup(struct inode *dir, struct dentry *dentry,
|
static int nfs4_proc_lookup(struct inode *dir, struct dentry *dentry, const struct qstr *name,
|
||||||
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
struct rpc_clnt *client = NFS_CLIENT(dir);
|
struct rpc_clnt *client = NFS_CLIENT(dir);
|
||||||
|
|
||||||
status = nfs4_proc_lookup_common(&client, dir, dentry, fhandle, fattr);
|
status = nfs4_proc_lookup_common(&client, dir, dentry, name, fhandle, fattr);
|
||||||
if (client != NFS_CLIENT(dir)) {
|
if (client != NFS_CLIENT(dir)) {
|
||||||
rpc_shutdown_client(client);
|
rpc_shutdown_client(client);
|
||||||
nfs_fixup_secinfo_attributes(fattr);
|
nfs_fixup_secinfo_attributes(fattr);
|
||||||
|
@ -4660,7 +4659,8 @@ nfs4_proc_lookup_mountpoint(struct inode *dir, struct dentry *dentry,
|
||||||
struct rpc_clnt *client = NFS_CLIENT(dir);
|
struct rpc_clnt *client = NFS_CLIENT(dir);
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
status = nfs4_proc_lookup_common(&client, dir, dentry, fhandle, fattr);
|
status = nfs4_proc_lookup_common(&client, dir, dentry, &dentry->d_name,
|
||||||
|
fhandle, fattr);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
return ERR_PTR(status);
|
return ERR_PTR(status);
|
||||||
return (client == NFS_CLIENT(dir)) ? rpc_clone_client(client) : client;
|
return (client == NFS_CLIENT(dir)) ? rpc_clone_client(client) : client;
|
||||||
|
|
|
@ -153,13 +153,13 @@ nfs_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
nfs_proc_lookup(struct inode *dir, struct dentry *dentry,
|
nfs_proc_lookup(struct inode *dir, struct dentry *dentry, const struct qstr *name,
|
||||||
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
||||||
{
|
{
|
||||||
struct nfs_diropargs arg = {
|
struct nfs_diropargs arg = {
|
||||||
.fh = NFS_FH(dir),
|
.fh = NFS_FH(dir),
|
||||||
.name = dentry->d_name.name,
|
.name = name->name,
|
||||||
.len = dentry->d_name.len
|
.len = name->len
|
||||||
};
|
};
|
||||||
struct nfs_diropok res = {
|
struct nfs_diropok res = {
|
||||||
.fh = fhandle,
|
.fh = fhandle,
|
||||||
|
|
|
@ -32,7 +32,8 @@ void ocfs2_dentry_attach_gen(struct dentry *dentry)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int ocfs2_dentry_revalidate(struct dentry *dentry, unsigned int flags)
|
static int ocfs2_dentry_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
int ret = 0; /* if all else fails, just return false */
|
int ret = 0; /* if all else fails, just return false */
|
||||||
|
@ -44,8 +45,7 @@ static int ocfs2_dentry_revalidate(struct dentry *dentry, unsigned int flags)
|
||||||
inode = d_inode(dentry);
|
inode = d_inode(dentry);
|
||||||
osb = OCFS2_SB(dentry->d_sb);
|
osb = OCFS2_SB(dentry->d_sb);
|
||||||
|
|
||||||
trace_ocfs2_dentry_revalidate(dentry, dentry->d_name.len,
|
trace_ocfs2_dentry_revalidate(dentry, name->len, name->name);
|
||||||
dentry->d_name.name);
|
|
||||||
|
|
||||||
/* For a negative dentry -
|
/* For a negative dentry -
|
||||||
* check the generation number of the parent and compare with the
|
* check the generation number of the parent and compare with the
|
||||||
|
@ -53,12 +53,8 @@ static int ocfs2_dentry_revalidate(struct dentry *dentry, unsigned int flags)
|
||||||
*/
|
*/
|
||||||
if (inode == NULL) {
|
if (inode == NULL) {
|
||||||
unsigned long gen = (unsigned long) dentry->d_fsdata;
|
unsigned long gen = (unsigned long) dentry->d_fsdata;
|
||||||
unsigned long pgen;
|
unsigned long pgen = OCFS2_I(dir)->ip_dir_lock_gen;
|
||||||
spin_lock(&dentry->d_lock);
|
trace_ocfs2_dentry_revalidate_negative(name->len, name->name,
|
||||||
pgen = OCFS2_I(d_inode(dentry->d_parent))->ip_dir_lock_gen;
|
|
||||||
spin_unlock(&dentry->d_lock);
|
|
||||||
trace_ocfs2_dentry_revalidate_negative(dentry->d_name.len,
|
|
||||||
dentry->d_name.name,
|
|
||||||
pgen, gen);
|
pgen, gen);
|
||||||
if (gen != pgen)
|
if (gen != pgen)
|
||||||
goto bail;
|
goto bail;
|
||||||
|
|
|
@ -13,10 +13,9 @@
|
||||||
#include "orangefs-kernel.h"
|
#include "orangefs-kernel.h"
|
||||||
|
|
||||||
/* Returns 1 if dentry can still be trusted, else 0. */
|
/* Returns 1 if dentry can still be trusted, else 0. */
|
||||||
static int orangefs_revalidate_lookup(struct dentry *dentry)
|
static int orangefs_revalidate_lookup(struct inode *parent_inode, const struct qstr *name,
|
||||||
|
struct dentry *dentry)
|
||||||
{
|
{
|
||||||
struct dentry *parent_dentry = dget_parent(dentry);
|
|
||||||
struct inode *parent_inode = parent_dentry->d_inode;
|
|
||||||
struct orangefs_inode_s *parent = ORANGEFS_I(parent_inode);
|
struct orangefs_inode_s *parent = ORANGEFS_I(parent_inode);
|
||||||
struct inode *inode = dentry->d_inode;
|
struct inode *inode = dentry->d_inode;
|
||||||
struct orangefs_kernel_op_s *new_op;
|
struct orangefs_kernel_op_s *new_op;
|
||||||
|
@ -26,14 +25,14 @@ static int orangefs_revalidate_lookup(struct dentry *dentry)
|
||||||
gossip_debug(GOSSIP_DCACHE_DEBUG, "%s: attempting lookup.\n", __func__);
|
gossip_debug(GOSSIP_DCACHE_DEBUG, "%s: attempting lookup.\n", __func__);
|
||||||
|
|
||||||
new_op = op_alloc(ORANGEFS_VFS_OP_LOOKUP);
|
new_op = op_alloc(ORANGEFS_VFS_OP_LOOKUP);
|
||||||
if (!new_op) {
|
if (!new_op)
|
||||||
ret = -ENOMEM;
|
return -ENOMEM;
|
||||||
goto out_put_parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
new_op->upcall.req.lookup.sym_follow = ORANGEFS_LOOKUP_LINK_NO_FOLLOW;
|
new_op->upcall.req.lookup.sym_follow = ORANGEFS_LOOKUP_LINK_NO_FOLLOW;
|
||||||
new_op->upcall.req.lookup.parent_refn = parent->refn;
|
new_op->upcall.req.lookup.parent_refn = parent->refn;
|
||||||
strscpy(new_op->upcall.req.lookup.d_name, dentry->d_name.name);
|
/* op_alloc() leaves ->upcall zeroed */
|
||||||
|
memcpy(new_op->upcall.req.lookup.d_name, name->name,
|
||||||
|
min(name->len, ORANGEFS_NAME_MAX - 1));
|
||||||
|
|
||||||
gossip_debug(GOSSIP_DCACHE_DEBUG,
|
gossip_debug(GOSSIP_DCACHE_DEBUG,
|
||||||
"%s:%s:%d interrupt flag [%d]\n",
|
"%s:%s:%d interrupt flag [%d]\n",
|
||||||
|
@ -78,8 +77,6 @@ static int orangefs_revalidate_lookup(struct dentry *dentry)
|
||||||
ret = 1;
|
ret = 1;
|
||||||
out_release_op:
|
out_release_op:
|
||||||
op_release(new_op);
|
op_release(new_op);
|
||||||
out_put_parent:
|
|
||||||
dput(parent_dentry);
|
|
||||||
return ret;
|
return ret;
|
||||||
out_drop:
|
out_drop:
|
||||||
gossip_debug(GOSSIP_DCACHE_DEBUG, "%s:%s:%d revalidate failed\n",
|
gossip_debug(GOSSIP_DCACHE_DEBUG, "%s:%s:%d revalidate failed\n",
|
||||||
|
@ -92,7 +89,8 @@ out_drop:
|
||||||
*
|
*
|
||||||
* Should return 1 if dentry can still be trusted, else 0.
|
* Should return 1 if dentry can still be trusted, else 0.
|
||||||
*/
|
*/
|
||||||
static int orangefs_d_revalidate(struct dentry *dentry, unsigned int flags)
|
static int orangefs_d_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
unsigned long time = (unsigned long) dentry->d_fsdata;
|
unsigned long time = (unsigned long) dentry->d_fsdata;
|
||||||
|
@ -114,7 +112,7 @@ static int orangefs_d_revalidate(struct dentry *dentry, unsigned int flags)
|
||||||
* If this passes, the positive dentry still exists or the negative
|
* If this passes, the positive dentry still exists or the negative
|
||||||
* dentry still does not exist.
|
* dentry still does not exist.
|
||||||
*/
|
*/
|
||||||
if (!orangefs_revalidate_lookup(dentry))
|
if (!orangefs_revalidate_lookup(dir, name, dentry))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* We do not need to continue with negative dentries. */
|
/* We do not need to continue with negative dentries. */
|
||||||
|
|
|
@ -91,7 +91,24 @@ static int ovl_revalidate_real(struct dentry *d, unsigned int flags, bool weak)
|
||||||
if (d->d_flags & DCACHE_OP_WEAK_REVALIDATE)
|
if (d->d_flags & DCACHE_OP_WEAK_REVALIDATE)
|
||||||
ret = d->d_op->d_weak_revalidate(d, flags);
|
ret = d->d_op->d_weak_revalidate(d, flags);
|
||||||
} else if (d->d_flags & DCACHE_OP_REVALIDATE) {
|
} else if (d->d_flags & DCACHE_OP_REVALIDATE) {
|
||||||
ret = d->d_op->d_revalidate(d, flags);
|
struct dentry *parent;
|
||||||
|
struct inode *dir;
|
||||||
|
struct name_snapshot n;
|
||||||
|
|
||||||
|
if (flags & LOOKUP_RCU) {
|
||||||
|
parent = READ_ONCE(d->d_parent);
|
||||||
|
dir = d_inode_rcu(parent);
|
||||||
|
if (!dir)
|
||||||
|
return -ECHILD;
|
||||||
|
} else {
|
||||||
|
parent = dget_parent(d);
|
||||||
|
dir = d_inode(parent);
|
||||||
|
}
|
||||||
|
take_dentry_name_snapshot(&n, d);
|
||||||
|
ret = d->d_op->d_revalidate(dir, &n.name, d, flags);
|
||||||
|
release_dentry_name_snapshot(&n);
|
||||||
|
if (!(flags & LOOKUP_RCU))
|
||||||
|
dput(parent);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
if (!(flags & LOOKUP_RCU))
|
if (!(flags & LOOKUP_RCU))
|
||||||
d_invalidate(d);
|
d_invalidate(d);
|
||||||
|
@ -127,7 +144,8 @@ static int ovl_dentry_revalidate_common(struct dentry *dentry,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ovl_dentry_revalidate(struct dentry *dentry, unsigned int flags)
|
static int ovl_dentry_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
return ovl_dentry_revalidate_common(dentry, flags, false);
|
return ovl_dentry_revalidate_common(dentry, flags, false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2058,7 +2058,8 @@ void pid_update_inode(struct task_struct *task, struct inode *inode)
|
||||||
* performed a setuid(), etc.
|
* performed a setuid(), etc.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static int pid_revalidate(struct dentry *dentry, unsigned int flags)
|
static int pid_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
struct task_struct *task;
|
struct task_struct *task;
|
||||||
|
@ -2191,7 +2192,8 @@ static int dname_to_vma_addr(struct dentry *dentry,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int map_files_d_revalidate(struct dentry *dentry, unsigned int flags)
|
static int map_files_d_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
unsigned long vm_start, vm_end;
|
unsigned long vm_start, vm_end;
|
||||||
bool exact_vma_exists = false;
|
bool exact_vma_exists = false;
|
||||||
|
|
|
@ -140,7 +140,8 @@ static void tid_fd_update_inode(struct task_struct *task, struct inode *inode,
|
||||||
security_task_to_inode(task, inode);
|
security_task_to_inode(task, inode);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tid_fd_revalidate(struct dentry *dentry, unsigned int flags)
|
static int tid_fd_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
struct task_struct *task;
|
struct task_struct *task;
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
|
|
|
@ -216,7 +216,8 @@ void proc_free_inum(unsigned int inum)
|
||||||
ida_free(&proc_inum_ida, inum - PROC_DYNAMIC_FIRST);
|
ida_free(&proc_inum_ida, inum - PROC_DYNAMIC_FIRST);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int proc_misc_d_revalidate(struct dentry *dentry, unsigned int flags)
|
static int proc_misc_d_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
if (flags & LOOKUP_RCU)
|
if (flags & LOOKUP_RCU)
|
||||||
return -ECHILD;
|
return -ECHILD;
|
||||||
|
@ -343,7 +344,8 @@ static const struct file_operations proc_dir_operations = {
|
||||||
.iterate_shared = proc_readdir,
|
.iterate_shared = proc_readdir,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int proc_net_d_revalidate(struct dentry *dentry, unsigned int flags)
|
static int proc_net_d_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -884,7 +884,8 @@ static const struct inode_operations proc_sys_dir_operations = {
|
||||||
.getattr = proc_sys_getattr,
|
.getattr = proc_sys_getattr,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int proc_sys_revalidate(struct dentry *dentry, unsigned int flags)
|
static int proc_sys_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
if (flags & LOOKUP_RCU)
|
if (flags & LOOKUP_RCU)
|
||||||
return -ECHILD;
|
return -ECHILD;
|
||||||
|
|
|
@ -737,7 +737,8 @@ again:
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
cifs_d_revalidate(struct dentry *direntry, unsigned int flags)
|
cifs_d_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *direntry, unsigned int flags)
|
||||||
{
|
{
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
|
@ -457,7 +457,8 @@ static void tracefs_d_release(struct dentry *dentry)
|
||||||
eventfs_d_release(dentry);
|
eventfs_d_release(dentry);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tracefs_d_revalidate(struct dentry *dentry, unsigned int flags)
|
static int tracefs_d_revalidate(struct inode *inode, const struct qstr *name,
|
||||||
|
struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
struct eventfs_inode *ei = dentry->d_fsdata;
|
struct eventfs_inode *ei = dentry->d_fsdata;
|
||||||
|
|
||||||
|
|
|
@ -192,7 +192,8 @@ const struct file_operations vboxsf_dir_fops = {
|
||||||
* This is called during name resolution/lookup to check if the @dentry in
|
* This is called during name resolution/lookup to check if the @dentry in
|
||||||
* the cache is still valid. the job is handled by vboxsf_inode_revalidate.
|
* the cache is still valid. the job is handled by vboxsf_inode_revalidate.
|
||||||
*/
|
*/
|
||||||
static int vboxsf_dentry_revalidate(struct dentry *dentry, unsigned int flags)
|
static int vboxsf_dentry_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
if (flags & LOOKUP_RCU)
|
if (flags & LOOKUP_RCU)
|
||||||
return -ECHILD;
|
return -ECHILD;
|
||||||
|
|
|
@ -68,16 +68,24 @@ extern const struct qstr dotdot_name;
|
||||||
* large memory footprint increase).
|
* large memory footprint increase).
|
||||||
*/
|
*/
|
||||||
#ifdef CONFIG_64BIT
|
#ifdef CONFIG_64BIT
|
||||||
# define DNAME_INLINE_LEN 40 /* 192 bytes */
|
# define DNAME_INLINE_WORDS 5 /* 192 bytes */
|
||||||
#else
|
#else
|
||||||
# ifdef CONFIG_SMP
|
# ifdef CONFIG_SMP
|
||||||
# define DNAME_INLINE_LEN 36 /* 128 bytes */
|
# define DNAME_INLINE_WORDS 9 /* 128 bytes */
|
||||||
# else
|
# else
|
||||||
# define DNAME_INLINE_LEN 44 /* 128 bytes */
|
# define DNAME_INLINE_WORDS 11 /* 128 bytes */
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define DNAME_INLINE_LEN (DNAME_INLINE_WORDS*sizeof(unsigned long))
|
||||||
|
|
||||||
|
union shortname_store {
|
||||||
|
unsigned char string[DNAME_INLINE_LEN];
|
||||||
|
unsigned long words[DNAME_INLINE_WORDS];
|
||||||
|
};
|
||||||
|
|
||||||
#define d_lock d_lockref.lock
|
#define d_lock d_lockref.lock
|
||||||
|
#define d_iname d_shortname.string
|
||||||
|
|
||||||
struct dentry {
|
struct dentry {
|
||||||
/* RCU lookup touched fields */
|
/* RCU lookup touched fields */
|
||||||
|
@ -88,7 +96,7 @@ struct dentry {
|
||||||
struct qstr d_name;
|
struct qstr d_name;
|
||||||
struct inode *d_inode; /* Where the name belongs to - NULL is
|
struct inode *d_inode; /* Where the name belongs to - NULL is
|
||||||
* negative */
|
* negative */
|
||||||
unsigned char d_iname[DNAME_INLINE_LEN]; /* small names */
|
union shortname_store d_shortname;
|
||||||
/* --- cacheline 1 boundary (64 bytes) was 32 bytes ago --- */
|
/* --- cacheline 1 boundary (64 bytes) was 32 bytes ago --- */
|
||||||
|
|
||||||
/* Ref lookup also touches following */
|
/* Ref lookup also touches following */
|
||||||
|
@ -136,7 +144,8 @@ enum d_real_type {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dentry_operations {
|
struct dentry_operations {
|
||||||
int (*d_revalidate)(struct dentry *, unsigned int);
|
int (*d_revalidate)(struct inode *, const struct qstr *,
|
||||||
|
struct dentry *, unsigned int);
|
||||||
int (*d_weak_revalidate)(struct dentry *, unsigned int);
|
int (*d_weak_revalidate)(struct dentry *, unsigned int);
|
||||||
int (*d_hash)(const struct dentry *, struct qstr *);
|
int (*d_hash)(const struct dentry *, struct qstr *);
|
||||||
int (*d_compare)(const struct dentry *,
|
int (*d_compare)(const struct dentry *,
|
||||||
|
@ -150,6 +159,8 @@ struct dentry_operations {
|
||||||
struct vfsmount *(*d_automount)(struct path *);
|
struct vfsmount *(*d_automount)(struct path *);
|
||||||
int (*d_manage)(const struct path *, bool);
|
int (*d_manage)(const struct path *, bool);
|
||||||
struct dentry *(*d_real)(struct dentry *, enum d_real_type type);
|
struct dentry *(*d_real)(struct dentry *, enum d_real_type type);
|
||||||
|
bool (*d_unalias_trylock)(const struct dentry *);
|
||||||
|
void (*d_unalias_unlock)(const struct dentry *);
|
||||||
} ____cacheline_aligned;
|
} ____cacheline_aligned;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -589,7 +600,7 @@ static inline struct inode *d_real_inode(const struct dentry *dentry)
|
||||||
|
|
||||||
struct name_snapshot {
|
struct name_snapshot {
|
||||||
struct qstr name;
|
struct qstr name;
|
||||||
unsigned char inline_name[DNAME_INLINE_LEN];
|
union shortname_store inline_name;
|
||||||
};
|
};
|
||||||
void take_dentry_name_snapshot(struct name_snapshot *, struct dentry *);
|
void take_dentry_name_snapshot(struct name_snapshot *, struct dentry *);
|
||||||
void release_dentry_name_snapshot(struct name_snapshot *);
|
void release_dentry_name_snapshot(struct name_snapshot *);
|
||||||
|
|
|
@ -192,7 +192,8 @@ struct fscrypt_operations {
|
||||||
unsigned int *num_devs);
|
unsigned int *num_devs);
|
||||||
};
|
};
|
||||||
|
|
||||||
int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags);
|
int fscrypt_d_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
|
struct dentry *dentry, unsigned int flags);
|
||||||
|
|
||||||
static inline struct fscrypt_inode_info *
|
static inline struct fscrypt_inode_info *
|
||||||
fscrypt_get_inode_info(const struct inode *inode)
|
fscrypt_get_inode_info(const struct inode *inode)
|
||||||
|
@ -711,8 +712,8 @@ static inline u64 fscrypt_fname_siphash(const struct inode *dir,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int fscrypt_d_revalidate(struct dentry *dentry,
|
static inline int fscrypt_d_revalidate(struct inode *dir, const struct qstr *name,
|
||||||
unsigned int flags)
|
struct dentry *dentry, unsigned int flags)
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1781,7 +1781,7 @@ struct nfs_rpc_ops {
|
||||||
struct nfs_fattr *, struct inode *);
|
struct nfs_fattr *, struct inode *);
|
||||||
int (*setattr) (struct dentry *, struct nfs_fattr *,
|
int (*setattr) (struct dentry *, struct nfs_fattr *,
|
||||||
struct iattr *);
|
struct iattr *);
|
||||||
int (*lookup) (struct inode *, struct dentry *,
|
int (*lookup) (struct inode *, struct dentry *, const struct qstr *,
|
||||||
struct nfs_fh *, struct nfs_fattr *);
|
struct nfs_fh *, struct nfs_fattr *);
|
||||||
int (*lookupp) (struct inode *, struct nfs_fh *,
|
int (*lookupp) (struct inode *, struct nfs_fh *,
|
||||||
struct nfs_fattr *);
|
struct nfs_fattr *);
|
||||||
|
|
|
@ -25,7 +25,7 @@ static long check_vma(struct task_struct *task, struct vm_area_struct *vma,
|
||||||
{
|
{
|
||||||
if (vma->vm_file)
|
if (vma->vm_file)
|
||||||
bpf_probe_read_kernel_str(d_iname, DNAME_INLINE_LEN - 1,
|
bpf_probe_read_kernel_str(d_iname, DNAME_INLINE_LEN - 1,
|
||||||
vma->vm_file->f_path.dentry->d_iname);
|
vma->vm_file->f_path.dentry->d_shortname.string);
|
||||||
|
|
||||||
/* check for VM_EXEC */
|
/* check for VM_EXEC */
|
||||||
if (vma->vm_flags & VM_EXEC)
|
if (vma->vm_flags & VM_EXEC)
|
||||||
|
|
Loading…
Add table
Reference in a new issue