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::
|
||||
|
||||
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_hash)(const struct dentry *, struct qstr *);
|
||||
int (*d_compare)(const struct dentry *,
|
||||
|
@ -30,6 +31,8 @@ prototypes::
|
|||
struct vfsmount *(*d_automount)(struct path *path);
|
||||
int (*d_manage)(const struct path *, bool);
|
||||
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:
|
||||
|
||||
|
@ -49,6 +52,8 @@ d_dname: no no no no
|
|||
d_automount: no no yes no
|
||||
d_manage: no no yes (ref-walk) maybe
|
||||
d_real no no yes no
|
||||
d_unalias_trylock yes no no no
|
||||
d_unalias_unlock yes no no no
|
||||
================== =========== ======== ============== ========
|
||||
|
||||
inode_operations
|
||||
|
|
|
@ -1141,3 +1141,19 @@ pointer are gone.
|
|||
|
||||
set_blocksize() takes opened struct file instead of struct block_device now
|
||||
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
|
||||
|
||||
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_hash)(const struct dentry *, struct qstr *);
|
||||
int (*d_compare)(const struct dentry *,
|
||||
|
@ -1264,6 +1265,8 @@ defined:
|
|||
struct vfsmount *(*d_automount)(struct path *);
|
||||
int (*d_manage)(const struct path *, bool);
|
||||
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``
|
||||
|
@ -1427,6 +1430,25 @@ defined:
|
|||
|
||||
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
|
||||
of child dentries. Child dentries are basically like files in a
|
||||
directory.
|
||||
|
|
|
@ -202,7 +202,7 @@ static inline struct v9fs_session_info *v9fs_inode2v9ses(struct inode *inode)
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ static void v9fs_dentry_release(struct dentry *dentry)
|
|||
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 inode *inode;
|
||||
|
@ -99,14 +99,36 @@ out_valid:
|
|||
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 = {
|
||||
.d_revalidate = v9fs_lookup_revalidate,
|
||||
.d_weak_revalidate = v9fs_lookup_revalidate,
|
||||
.d_weak_revalidate = __v9fs_lookup_revalidate,
|
||||
.d_delete = v9fs_cached_dentry_delete,
|
||||
.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 = {
|
||||
.d_delete = always_delete_dentry,
|
||||
.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);
|
||||
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_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 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,
|
||||
|
@ -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
|
||||
* - 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,
|
||||
afs_dataversion_t *_dir_version)
|
||||
{
|
||||
struct afs_super_info *as = dir->i_sb->s_fs_info;
|
||||
struct afs_lookup_one_cookie cookie = {
|
||||
.ctx.actor = afs_lookup_one_filldir,
|
||||
.name = dentry->d_name,
|
||||
.name = *name,
|
||||
.fid.vid = as->volume->vid
|
||||
};
|
||||
int ret;
|
||||
|
||||
_enter("{%lu},%p{%pd},", dir->i_ino, dentry, dentry);
|
||||
_enter("{%lu},{%.*s},", dir->i_ino, name->len, name->name);
|
||||
|
||||
/* search the directory */
|
||||
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.
|
||||
*/
|
||||
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;
|
||||
|
||||
_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))
|
||||
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
|
||||
* 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 dentry *parent;
|
||||
struct inode *inode;
|
||||
struct key *key;
|
||||
afs_dataversion_t dir_version, invalid_before;
|
||||
|
@ -1077,7 +1069,7 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
|
|||
int ret;
|
||||
|
||||
if (flags & LOOKUP_RCU)
|
||||
return afs_d_revalidate_rcu(dentry);
|
||||
return afs_d_revalidate_rcu(dir, dentry);
|
||||
|
||||
if (d_really_is_positive(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))
|
||||
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 */
|
||||
ret = afs_validate(dir, key);
|
||||
if (ret == -ERESTARTSYS) {
|
||||
dput(parent);
|
||||
key_put(key);
|
||||
return ret;
|
||||
}
|
||||
|
@ -1127,7 +1114,7 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
|
|||
afs_stat_v(dir, n_reval);
|
||||
|
||||
/* 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) {
|
||||
case 0:
|
||||
/* the filename maps to something */
|
||||
|
@ -1171,22 +1158,19 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
|
|||
goto out_valid;
|
||||
|
||||
default:
|
||||
_debug("failed to iterate dir %pd: %d",
|
||||
parent, ret);
|
||||
_debug("failed to iterate parent %pd2: %d", dentry, ret);
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
out_valid:
|
||||
dentry->d_fsdata = (void *)(unsigned long)dir_version;
|
||||
out_valid_noupdate:
|
||||
dput(parent);
|
||||
key_put(key);
|
||||
_leave(" = 1 [valid]");
|
||||
return 1;
|
||||
|
||||
not_found:
|
||||
_debug("dropping dentry %pd2", dentry);
|
||||
dput(parent);
|
||||
key_put(key);
|
||||
|
||||
_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.
|
||||
*/
|
||||
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_client *cl = mdsc->fsc->client;
|
||||
int valid = 0;
|
||||
struct dentry *parent;
|
||||
struct inode *dir, *inode;
|
||||
struct inode *inode;
|
||||
|
||||
valid = fscrypt_d_revalidate(dentry, flags);
|
||||
valid = fscrypt_d_revalidate(dir, name, dentry, flags);
|
||||
if (valid <= 0)
|
||||
return valid;
|
||||
|
||||
if (flags & LOOKUP_RCU) {
|
||||
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);
|
||||
}
|
||||
inode = d_inode_rcu(dentry);
|
||||
|
||||
doutc(cl, "%p '%pd' inode %p offset 0x%llx nokey %d\n",
|
||||
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;
|
||||
ihold(dir);
|
||||
|
||||
req->r_dname = name;
|
||||
|
||||
mask = CEPH_STAT_CAP_INODE | CEPH_CAP_AUTH_SHARED;
|
||||
if (ceph_security_xattr_wanted(dir))
|
||||
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");
|
||||
if (!valid)
|
||||
ceph_dir_clear_complete(dir);
|
||||
|
||||
if (!(flags & LOOKUP_RCU))
|
||||
dput(parent);
|
||||
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 dentry *dentry = req->r_dentry;
|
||||
const struct qstr *name = req->r_dname;
|
||||
u8 *cryptbuf = NULL;
|
||||
u32 len = 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))
|
||||
goto success;
|
||||
|
||||
if (!fscrypt_fname_encrypted_size(dir, dentry->d_name.len, NAME_MAX,
|
||||
&len)) {
|
||||
if (!name)
|
||||
name = &dentry->d_name;
|
||||
|
||||
if (!fscrypt_fname_encrypted_size(dir, name->len, NAME_MAX, &len)) {
|
||||
WARN_ON_ONCE(1);
|
||||
return ERR_PTR(-ENAMETOOLONG);
|
||||
}
|
||||
|
@ -2657,7 +2660,7 @@ static u8 *get_fscrypt_altname(const struct ceph_mds_request *req, u32 *plen)
|
|||
if (!cryptbuf)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ret = fscrypt_fname_encrypt(dir, &dentry->d_name, cryptbuf, len);
|
||||
ret = fscrypt_fname_encrypt(dir, name, cryptbuf, len);
|
||||
if (ret) {
|
||||
kfree(cryptbuf);
|
||||
return ERR_PTR(ret);
|
||||
|
|
|
@ -299,6 +299,8 @@ struct ceph_mds_request {
|
|||
struct inode *r_target_inode; /* resulting inode */
|
||||
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_ABORTED (2) /* call was aborted */
|
||||
#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 */
|
||||
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 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
|
||||
* 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 valid;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Although fscrypt forbids rename() on no-key names, we still must use
|
||||
* dget_parent() here rather than use ->d_parent directly. That's
|
||||
* 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.
|
||||
* Note in RCU mode we have to bail if we get here -
|
||||
* fscrypt_get_encryption_info() may block.
|
||||
*/
|
||||
|
||||
if (flags & LOOKUP_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
dir = dget_parent(dentry);
|
||||
/*
|
||||
* Pass allow_unsupported=true, so that files with an unsupported
|
||||
* encryption policy can be deleted.
|
||||
*/
|
||||
err = fscrypt_get_encryption_info(d_inode(dir), true);
|
||||
valid = !fscrypt_has_encryption_key(d_inode(dir));
|
||||
dput(dir);
|
||||
|
||||
err = fscrypt_get_encryption_info(dir, true);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return valid;
|
||||
return !fscrypt_has_encryption_key(dir);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 {
|
||||
union {
|
||||
atomic_t count;
|
||||
struct rcu_head head;
|
||||
} u;
|
||||
unsigned char name[];
|
||||
atomic_t count;
|
||||
struct rcu_head head;
|
||||
unsigned char name[] __aligned(sizeof(unsigned long));
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
spin_lock(&dentry->d_lock);
|
||||
name->name = dentry->d_name;
|
||||
if (unlikely(dname_external(dentry))) {
|
||||
atomic_inc(&external_name(dentry)->u.count);
|
||||
unsigned seq;
|
||||
const unsigned char *s;
|
||||
|
||||
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 {
|
||||
memcpy(name->inline_name, dentry->d_iname,
|
||||
dentry->d_name.len + 1);
|
||||
name->name.name = name->inline_name;
|
||||
struct external_name *p;
|
||||
p = container_of(s, struct external_name, name[0]);
|
||||
// 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);
|
||||
|
||||
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;
|
||||
p = container_of(name->name.name, struct external_name, name[0]);
|
||||
if (unlikely(atomic_dec_and_test(&p->u.count)))
|
||||
kfree_rcu(p, u.head);
|
||||
if (unlikely(atomic_dec_and_test(&p->count)))
|
||||
kfree_rcu(p, head);
|
||||
}
|
||||
}
|
||||
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));
|
||||
if (unlikely(dname_external(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);
|
||||
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
|
||||
* 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)) {
|
||||
name = &slash_name;
|
||||
dname = dentry->d_iname;
|
||||
dname = dentry->d_shortname.string;
|
||||
} else if (name->len > DNAME_INLINE_LEN-1) {
|
||||
size_t size = offsetof(struct external_name, name[1]);
|
||||
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);
|
||||
return NULL;
|
||||
}
|
||||
atomic_set(&p->u.count, 1);
|
||||
atomic_set(&p->count, 1);
|
||||
dname = p->name;
|
||||
} else {
|
||||
dname = dentry->d_iname;
|
||||
dname = dentry->d_shortname.string;
|
||||
}
|
||||
|
||||
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
|
||||
* 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;
|
||||
target->d_name.name = target->d_iname;
|
||||
target->d_shortname = dentry->d_shortname;
|
||||
target->d_name.name = target->d_shortname.string;
|
||||
}
|
||||
} else {
|
||||
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
|
||||
* 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;
|
||||
dentry->d_name.name = dentry->d_iname;
|
||||
dentry->d_shortname = target->d_shortname;
|
||||
dentry->d_name.name = dentry->d_shortname.string;
|
||||
} else {
|
||||
/*
|
||||
* Both are internal.
|
||||
*/
|
||||
unsigned int i;
|
||||
BUILD_BUG_ON(!IS_ALIGNED(DNAME_INLINE_LEN, sizeof(long)));
|
||||
for (i = 0; i < DNAME_INLINE_LEN / sizeof(long); i++) {
|
||||
swap(((long *) &dentry->d_iname)[i],
|
||||
((long *) &target->d_iname)[i]);
|
||||
}
|
||||
for (int i = 0; i < DNAME_INLINE_WORDS; i++)
|
||||
swap(dentry->d_shortname.words[i],
|
||||
target->d_shortname.words[i]);
|
||||
}
|
||||
}
|
||||
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)))
|
||||
old_name = external_name(dentry);
|
||||
if (unlikely(dname_external(target))) {
|
||||
atomic_inc(&external_name(target)->u.count);
|
||||
atomic_inc(&external_name(target)->count);
|
||||
dentry->d_name = target->d_name;
|
||||
} else {
|
||||
memcpy(dentry->d_iname, target->d_name.name,
|
||||
target->d_name.len + 1);
|
||||
dentry->d_name.name = dentry->d_iname;
|
||||
dentry->d_shortname = target->d_shortname;
|
||||
dentry->d_name.name = dentry->d_shortname.string;
|
||||
dentry->d_name.hash_len = target->d_name.hash_len;
|
||||
}
|
||||
if (old_name && likely(atomic_dec_and_test(&old_name->u.count)))
|
||||
kfree_rcu(old_name, u.head);
|
||||
if (old_name && likely(atomic_dec_and_test(&old_name->count)))
|
||||
kfree_rcu(old_name, head);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2954,7 +2966,12 @@ static int __d_unalias(struct dentry *dentry, struct dentry *alias)
|
|||
goto out_err;
|
||||
m2 = &alias->d_parent->d_inode->i_rwsem;
|
||||
out_unalias:
|
||||
if (alias->d_op->d_unalias_trylock &&
|
||||
!alias->d_op->d_unalias_trylock(alias))
|
||||
goto out_err;
|
||||
__d_move(alias, dentry, false);
|
||||
if (alias->d_op->d_unalias_unlock)
|
||||
alias->d_op->d_unalias_unlock(alias);
|
||||
ret = 0;
|
||||
out_err:
|
||||
if (m2)
|
||||
|
@ -3102,12 +3119,12 @@ void d_mark_tmpfile(struct file *file, struct inode *inode)
|
|||
{
|
||||
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) ||
|
||||
!d_unlinked(dentry));
|
||||
spin_lock(&dentry->d_parent->d_lock);
|
||||
spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
|
||||
dentry->d_name.len = sprintf(dentry->d_iname, "#%llu",
|
||||
dentry->d_name.len = sprintf(dentry->d_shortname.string, "#%llu",
|
||||
(unsigned long long)inode->i_ino);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_unlock(&dentry->d_parent->d_lock);
|
||||
|
@ -3195,7 +3212,7 @@ static void __init dcache_init(void)
|
|||
*/
|
||||
dentry_cache = KMEM_CACHE_USERCOPY(dentry,
|
||||
SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|SLAB_ACCOUNT,
|
||||
d_iname);
|
||||
d_shortname.string);
|
||||
|
||||
/* Hash may have been set up in dcache_init_early */
|
||||
if (!hashdist)
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* Called when the VFS needs to revalidate a dentry. This
|
||||
|
@ -28,7 +30,8 @@
|
|||
* 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);
|
||||
int rc = 1;
|
||||
|
@ -36,8 +39,15 @@ static int ecryptfs_d_revalidate(struct dentry *dentry, unsigned int flags)
|
|||
if (flags & LOOKUP_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
if (lower_dentry->d_flags & DCACHE_OP_REVALIDATE)
|
||||
rc = lower_dentry->d_op->d_revalidate(lower_dentry, flags);
|
||||
if (lower_dentry->d_flags & DCACHE_OP_REVALIDATE) {
|
||||
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)) {
|
||||
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,
|
||||
* 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)
|
||||
return -ECHILD;
|
||||
|
||||
|
@ -58,11 +57,7 @@ static int exfat_d_revalidate(struct dentry *dentry, unsigned int flags)
|
|||
if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET))
|
||||
return 0;
|
||||
|
||||
spin_lock(&dentry->d_lock);
|
||||
ret = inode_eq_iversion(d_inode(dentry->d_parent),
|
||||
exfat_d_version(dentry));
|
||||
spin_unlock(&dentry->d_lock);
|
||||
return ret;
|
||||
return inode_eq_iversion(dir, exfat_d_version(dentry));
|
||||
}
|
||||
|
||||
/* 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));
|
||||
spin_unlock(&sbi->s_fc_lock);
|
||||
|
||||
if (fc_dentry->fcd_name.name &&
|
||||
fc_dentry->fcd_name.len > DNAME_INLINE_LEN)
|
||||
kfree(fc_dentry->fcd_name.name);
|
||||
release_dentry_name_snapshot(&fc_dentry->fcd_name);
|
||||
kmem_cache_free(ext4_fc_dentry_cachep, fc_dentry);
|
||||
|
||||
return;
|
||||
|
@ -449,22 +447,7 @@ static int __track_dentry_update(handle_t *handle, struct inode *inode,
|
|||
node->fcd_op = dentry_update->op;
|
||||
node->fcd_parent = dir->i_ino;
|
||||
node->fcd_ino = inode->i_ino;
|
||||
if (dentry->d_name.len > DNAME_INLINE_LEN) {
|
||||
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;
|
||||
take_dentry_name_snapshot(&node->fcd_name, dentry);
|
||||
INIT_LIST_HEAD(&node->fcd_dilist);
|
||||
spin_lock(&sbi->s_fc_lock);
|
||||
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_tl tl;
|
||||
int dlen = fc_dentry->fcd_name.len;
|
||||
int dlen = fc_dentry->fcd_name.name.len;
|
||||
u8 *dst = ext4_fc_reserve_space(sb,
|
||||
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;
|
||||
memcpy(dst, &fcd, sizeof(fcd));
|
||||
dst += sizeof(fcd);
|
||||
memcpy(dst, fc_dentry->fcd_name.name, dlen);
|
||||
memcpy(dst, fc_dentry->fcd_name.name.name, dlen);
|
||||
|
||||
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);
|
||||
spin_unlock(&sbi->s_fc_lock);
|
||||
|
||||
if (fc_dentry->fcd_name.name &&
|
||||
fc_dentry->fcd_name.len > DNAME_INLINE_LEN)
|
||||
kfree(fc_dentry->fcd_name.name);
|
||||
release_dentry_name_snapshot(&fc_dentry->fcd_name);
|
||||
kmem_cache_free(ext4_fc_dentry_cachep, fc_dentry);
|
||||
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_parent; /* Parent inode number */
|
||||
int fcd_ino; /* Inode number */
|
||||
struct qstr fcd_name; /* Dirent name */
|
||||
unsigned char fcd_iname[DNAME_INLINE_LEN]; /* Dirent name string */
|
||||
struct name_snapshot fcd_name; /* Dirent name */
|
||||
struct list_head fcd_list;
|
||||
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
|
||||
* 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;
|
||||
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;
|
||||
return inode_eq_iversion(dir, vfat_d_version(dentry));
|
||||
}
|
||||
|
||||
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)
|
||||
return -ECHILD;
|
||||
|
@ -61,10 +57,11 @@ static int vfat_revalidate(struct dentry *dentry, unsigned int flags)
|
|||
/* This is not negative dentry. Always valid. */
|
||||
if (d_really_is_positive(dentry))
|
||||
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)
|
||||
return -ECHILD;
|
||||
|
@ -97,7 +94,7 @@ static int vfat_revalidate_ci(struct dentry *dentry, unsigned int flags)
|
|||
if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET))
|
||||
return 0;
|
||||
|
||||
return vfat_revalidate_shortname(dentry);
|
||||
return vfat_revalidate_shortname(dentry, dir);
|
||||
}
|
||||
|
||||
/* 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));
|
||||
args->opcode = FUSE_LOOKUP;
|
||||
args->nodeid = nodeid;
|
||||
args->in_numargs = 2;
|
||||
args->in_numargs = 3;
|
||||
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[2].size = 1;
|
||||
args->in_args[2].value = "";
|
||||
args->out_numargs = 1;
|
||||
args->out_args[0].size = sizeof(struct fuse_entry_out);
|
||||
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,
|
||||
* 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 dentry *parent;
|
||||
struct fuse_mount *fm;
|
||||
struct fuse_inode *fi;
|
||||
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);
|
||||
|
||||
parent = dget_parent(entry);
|
||||
fuse_lookup_init(fm->fc, &args, get_node_id(d_inode(parent)),
|
||||
&entry->d_name, &outarg);
|
||||
fuse_lookup_init(fm->fc, &args, get_node_id(dir),
|
||||
name, &outarg);
|
||||
ret = fuse_simple_request(fm, &args);
|
||||
dput(parent);
|
||||
/* Zero nodeid is same as -ENOENT */
|
||||
if (!ret && !outarg.nodeid)
|
||||
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))
|
||||
return -ECHILD;
|
||||
} else if (test_and_clear_bit(FUSE_I_INIT_RDPLUS, &fi->state)) {
|
||||
parent = dget_parent(entry);
|
||||
fuse_advise_use_readdirplus(d_inode(parent));
|
||||
dput(parent);
|
||||
fuse_advise_use_readdirplus(dir);
|
||||
}
|
||||
}
|
||||
ret = 1;
|
||||
|
|
|
@ -21,7 +21,9 @@
|
|||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
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;
|
||||
struct gfs2_inode *dip;
|
||||
struct gfs2_sbd *sdp = GFS2_SB(dir);
|
||||
struct gfs2_inode *dip = GFS2_I(dir);
|
||||
struct inode *inode;
|
||||
struct gfs2_holder d_gh;
|
||||
struct gfs2_inode *ip = NULL;
|
||||
int error, valid = 0;
|
||||
int error, valid;
|
||||
int had_lock = 0;
|
||||
|
||||
if (flags & LOOKUP_RCU)
|
||||
return -ECHILD;
|
||||
|
||||
parent = dget_parent(dentry);
|
||||
sdp = GFS2_SB(d_inode(parent));
|
||||
dip = GFS2_I(d_inode(parent));
|
||||
inode = d_inode(dentry);
|
||||
|
||||
if (inode) {
|
||||
if (is_bad_inode(inode))
|
||||
goto out;
|
||||
return 0;
|
||||
ip = GFS2_I(inode);
|
||||
}
|
||||
|
||||
if (sdp->sd_lockstruct.ls_ops->lm_mount == NULL) {
|
||||
valid = 1;
|
||||
goto out;
|
||||
}
|
||||
if (sdp->sd_lockstruct.ls_ops->lm_mount == NULL)
|
||||
return 1;
|
||||
|
||||
had_lock = (gfs2_glock_is_locked_by_me(dip->i_gl) != NULL);
|
||||
if (!had_lock) {
|
||||
error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &d_gh);
|
||||
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);
|
||||
|
||||
if (!had_lock)
|
||||
gfs2_glock_dq_uninit(&d_gh);
|
||||
out:
|
||||
dput(parent);
|
||||
return valid;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
|
||||
/* 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;
|
||||
int diff;
|
||||
|
|
|
@ -1576,7 +1576,8 @@ out:
|
|||
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.
|
||||
|
|
|
@ -1109,7 +1109,8 @@ struct kernfs_node *kernfs_create_empty_dir(struct kernfs_node *parent,
|
|||
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_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 inode *dir;
|
||||
char strbuf[DNAME_INLINE_LEN];
|
||||
union shortname_store strbuf;
|
||||
struct qstr qstr;
|
||||
|
||||
/*
|
||||
|
@ -1802,22 +1802,23 @@ int generic_ci_d_compare(const struct dentry *dentry, unsigned int len,
|
|||
if (!dir || !IS_CASEFOLDED(dir))
|
||||
return 1;
|
||||
|
||||
qstr.len = len;
|
||||
qstr.name = str;
|
||||
/*
|
||||
* 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
|
||||
* the lookup, so it doesn't matter what ->d_compare() returns.
|
||||
* However, it's unsafe to call utf8_strncasecmp() with an unstable
|
||||
* 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) {
|
||||
memcpy(strbuf, str, len);
|
||||
strbuf[len] = 0;
|
||||
str = strbuf;
|
||||
if (qstr.name == dentry->d_shortname.string) {
|
||||
strbuf = dentry->d_shortname; // NUL is guaranteed to be in there
|
||||
qstr.name = strbuf.string;
|
||||
/* prevent compiler from optimizing out the temporary buffer */
|
||||
barrier();
|
||||
}
|
||||
qstr.len = len;
|
||||
qstr.name = str;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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))
|
||||
return dentry->d_op->d_revalidate(dentry, flags);
|
||||
return dentry->d_op->d_revalidate(dir, name, dentry, flags);
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
@ -1652,7 +1653,7 @@ static struct dentry *lookup_dcache(const struct qstr *name,
|
|||
{
|
||||
struct dentry *dentry = d_lookup(dir, name);
|
||||
if (dentry) {
|
||||
int error = d_revalidate(dentry, flags);
|
||||
int error = d_revalidate(dir->d_inode, name, dentry, flags);
|
||||
if (unlikely(error <= 0)) {
|
||||
if (!error)
|
||||
d_invalidate(dentry);
|
||||
|
@ -1737,19 +1738,20 @@ static struct dentry *lookup_fast(struct nameidata *nd)
|
|||
if (read_seqcount_retry(&parent->d_seq, nd->seq))
|
||||
return ERR_PTR(-ECHILD);
|
||||
|
||||
status = d_revalidate(dentry, nd->flags);
|
||||
status = d_revalidate(nd->inode, &nd->last, dentry, nd->flags);
|
||||
if (likely(status > 0))
|
||||
return dentry;
|
||||
if (!try_to_unlazy_next(nd, dentry))
|
||||
return ERR_PTR(-ECHILD);
|
||||
if (status == -ECHILD)
|
||||
/* 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 {
|
||||
dentry = __d_lookup(parent, &nd->last);
|
||||
if (unlikely(!dentry))
|
||||
return NULL;
|
||||
status = d_revalidate(dentry, nd->flags);
|
||||
status = d_revalidate(nd->inode, &nd->last, dentry, nd->flags);
|
||||
}
|
||||
if (unlikely(status <= 0)) {
|
||||
if (!status)
|
||||
|
@ -1777,7 +1779,7 @@ again:
|
|||
if (IS_ERR(dentry))
|
||||
return 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 (!error) {
|
||||
d_invalidate(dentry);
|
||||
|
@ -3575,7 +3577,7 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
|
|||
if (d_in_lookup(dentry))
|
||||
break;
|
||||
|
||||
error = d_revalidate(dentry, nd->flags);
|
||||
error = d_revalidate(dir_inode, &nd->last, dentry, nd->flags);
|
||||
if (likely(error > 0))
|
||||
break;
|
||||
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);
|
||||
}
|
||||
|
||||
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 inode *inode, unsigned int flags)
|
||||
{
|
||||
|
@ -1690,7 +1690,7 @@ static int nfs_lookup_revalidate_dentry(struct inode *dir,
|
|||
goto out;
|
||||
|
||||
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)
|
||||
goto out;
|
||||
|
||||
|
@ -1732,8 +1732,8 @@ out:
|
|||
* cached dentry and do a new lookup.
|
||||
*/
|
||||
static int
|
||||
nfs_do_lookup_revalidate(struct inode *dir, struct dentry *dentry,
|
||||
unsigned int flags)
|
||||
nfs_do_lookup_revalidate(struct inode *dir, const struct qstr *name,
|
||||
struct dentry *dentry, unsigned int flags)
|
||||
{
|
||||
struct inode *inode;
|
||||
int error = 0;
|
||||
|
@ -1775,7 +1775,7 @@ nfs_do_lookup_revalidate(struct inode *dir, struct dentry *dentry,
|
|||
if (NFS_STALE(inode))
|
||||
goto out_bad;
|
||||
|
||||
return nfs_lookup_revalidate_dentry(dir, dentry, inode, flags);
|
||||
return nfs_lookup_revalidate_dentry(dir, name, dentry, inode, flags);
|
||||
out_valid:
|
||||
return nfs_lookup_revalidate_done(dir, dentry, inode, 1);
|
||||
out_bad:
|
||||
|
@ -1785,38 +1785,26 @@ out_bad:
|
|||
}
|
||||
|
||||
static int
|
||||
__nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags,
|
||||
int (*reval)(struct inode *, struct dentry *, unsigned int))
|
||||
__nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
|
||||
{
|
||||
struct dentry *parent;
|
||||
struct inode *dir;
|
||||
int ret;
|
||||
|
||||
if (flags & LOOKUP_RCU) {
|
||||
if (dentry->d_fsdata == NFS_FSDATA_BLOCKED)
|
||||
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 {
|
||||
/* Wait for unlink to complete - see unblock_revalidate() */
|
||||
wait_var_event(&dentry->d_fsdata,
|
||||
smp_load_acquire(&dentry->d_fsdata)
|
||||
!= 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)
|
||||
|
@ -1982,7 +1970,8 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in
|
|||
|
||||
dir_verifier = nfs_save_change_attribute(dir);
|
||||
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 (nfs_server_capable(dir, NFS_CAP_CASE_INSENSITIVE))
|
||||
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);
|
||||
|
||||
#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 = {
|
||||
.d_revalidate = nfs4_lookup_revalidate,
|
||||
|
@ -2214,11 +2204,14 @@ no_open:
|
|||
EXPORT_SYMBOL_GPL(nfs_atomic_open);
|
||||
|
||||
static int
|
||||
nfs4_do_lookup_revalidate(struct inode *dir, struct dentry *dentry,
|
||||
unsigned int flags)
|
||||
nfs4_lookup_revalidate(struct inode *dir, const struct qstr *name,
|
||||
struct dentry *dentry, unsigned int flags)
|
||||
{
|
||||
struct inode *inode;
|
||||
|
||||
if (__nfs_lookup_revalidate(dentry, flags))
|
||||
return -ECHILD;
|
||||
|
||||
trace_nfs_lookup_revalidate_enter(dir, dentry, flags);
|
||||
|
||||
if (!(flags & LOOKUP_OPEN) || (flags & LOOKUP_DIRECTORY))
|
||||
|
@ -2254,16 +2247,10 @@ nfs4_do_lookup_revalidate(struct inode *dir, struct dentry *dentry,
|
|||
reval_dentry:
|
||||
if (flags & LOOKUP_RCU)
|
||||
return -ECHILD;
|
||||
return nfs_lookup_revalidate_dentry(dir, dentry, inode, flags);
|
||||
return nfs_lookup_revalidate_dentry(dir, name, dentry, inode, flags);
|
||||
|
||||
full_reval:
|
||||
return nfs_do_lookup_revalidate(dir, dentry, flags);
|
||||
}
|
||||
|
||||
static int nfs4_lookup_revalidate(struct dentry *dentry, unsigned int flags)
|
||||
{
|
||||
return __nfs_lookup_revalidate(dentry, flags,
|
||||
nfs4_do_lookup_revalidate);
|
||||
return nfs_do_lookup_revalidate(dir, name, dentry, flags);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_NFSV4 */
|
||||
|
@ -2319,7 +2306,8 @@ nfs_add_or_obtain(struct dentry *dentry, struct nfs_fh *fhandle,
|
|||
d_drop(dentry);
|
||||
|
||||
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)
|
||||
goto out_error;
|
||||
}
|
||||
|
|
|
@ -308,7 +308,7 @@ int nfs_submount(struct fs_context *fc, struct nfs_server *server)
|
|||
int err;
|
||||
|
||||
/* 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);
|
||||
dput(parent);
|
||||
if (err != 0)
|
||||
|
|
|
@ -192,7 +192,7 @@ __nfs3_proc_lookup(struct inode *dir, const char *name, size_t len,
|
|||
}
|
||||
|
||||
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)
|
||||
{
|
||||
unsigned short task_flags = 0;
|
||||
|
@ -202,8 +202,7 @@ nfs3_proc_lookup(struct inode *dir, struct dentry *dentry,
|
|||
task_flags |= RPC_TASK_TIMEOUT;
|
||||
|
||||
dprintk("NFS call lookup %pd2\n", dentry);
|
||||
return __nfs3_proc_lookup(dir, dentry->d_name.name,
|
||||
dentry->d_name.len, fhandle, fattr,
|
||||
return __nfs3_proc_lookup(dir, name->name, name->len, fhandle, fattr,
|
||||
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,
|
||||
struct dentry *dentry, struct nfs_fh *fhandle,
|
||||
struct nfs_fattr *fattr)
|
||||
struct dentry *dentry, const struct qstr *name,
|
||||
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
||||
{
|
||||
struct nfs_server *server = NFS_SERVER(dir);
|
||||
int status;
|
||||
struct nfs4_lookup_arg args = {
|
||||
.bitmask = server->attr_bitmask,
|
||||
.dir_fh = NFS_FH(dir),
|
||||
.name = &dentry->d_name,
|
||||
.name = name,
|
||||
};
|
||||
struct nfs4_lookup_res res = {
|
||||
.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,
|
||||
struct dentry *dentry, struct nfs_fh *fhandle,
|
||||
struct nfs_fattr *fattr)
|
||||
struct dentry *dentry, const struct qstr *name,
|
||||
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
||||
{
|
||||
struct nfs4_exception exception = {
|
||||
.interruptible = true,
|
||||
};
|
||||
struct rpc_clnt *client = *clnt;
|
||||
const struct qstr *name = &dentry->d_name;
|
||||
int err;
|
||||
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);
|
||||
switch (err) {
|
||||
case -NFS4ERR_BADNAME:
|
||||
|
@ -4639,13 +4638,13 @@ out:
|
|||
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)
|
||||
{
|
||||
int status;
|
||||
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)) {
|
||||
rpc_shutdown_client(client);
|
||||
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);
|
||||
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)
|
||||
return ERR_PTR(status);
|
||||
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
|
||||
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_diropargs arg = {
|
||||
.fh = NFS_FH(dir),
|
||||
.name = dentry->d_name.name,
|
||||
.len = dentry->d_name.len
|
||||
.name = name->name,
|
||||
.len = name->len
|
||||
};
|
||||
struct nfs_diropok res = {
|
||||
.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;
|
||||
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);
|
||||
osb = OCFS2_SB(dentry->d_sb);
|
||||
|
||||
trace_ocfs2_dentry_revalidate(dentry, dentry->d_name.len,
|
||||
dentry->d_name.name);
|
||||
trace_ocfs2_dentry_revalidate(dentry, name->len, name->name);
|
||||
|
||||
/* For a negative dentry -
|
||||
* 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) {
|
||||
unsigned long gen = (unsigned long) dentry->d_fsdata;
|
||||
unsigned long pgen;
|
||||
spin_lock(&dentry->d_lock);
|
||||
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,
|
||||
unsigned long pgen = OCFS2_I(dir)->ip_dir_lock_gen;
|
||||
trace_ocfs2_dentry_revalidate_negative(name->len, name->name,
|
||||
pgen, gen);
|
||||
if (gen != pgen)
|
||||
goto bail;
|
||||
|
|
|
@ -13,10 +13,9 @@
|
|||
#include "orangefs-kernel.h"
|
||||
|
||||
/* 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 inode *inode = dentry->d_inode;
|
||||
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__);
|
||||
|
||||
new_op = op_alloc(ORANGEFS_VFS_OP_LOOKUP);
|
||||
if (!new_op) {
|
||||
ret = -ENOMEM;
|
||||
goto out_put_parent;
|
||||
}
|
||||
if (!new_op)
|
||||
return -ENOMEM;
|
||||
|
||||
new_op->upcall.req.lookup.sym_follow = ORANGEFS_LOOKUP_LINK_NO_FOLLOW;
|
||||
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,
|
||||
"%s:%s:%d interrupt flag [%d]\n",
|
||||
|
@ -78,8 +77,6 @@ static int orangefs_revalidate_lookup(struct dentry *dentry)
|
|||
ret = 1;
|
||||
out_release_op:
|
||||
op_release(new_op);
|
||||
out_put_parent:
|
||||
dput(parent_dentry);
|
||||
return ret;
|
||||
out_drop:
|
||||
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.
|
||||
*/
|
||||
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;
|
||||
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
|
||||
* dentry still does not exist.
|
||||
*/
|
||||
if (!orangefs_revalidate_lookup(dentry))
|
||||
if (!orangefs_revalidate_lookup(dir, name, dentry))
|
||||
return 0;
|
||||
|
||||
/* 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)
|
||||
ret = d->d_op->d_weak_revalidate(d, flags);
|
||||
} 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 (!(flags & LOOKUP_RCU))
|
||||
d_invalidate(d);
|
||||
|
@ -127,7 +144,8 @@ static int ovl_dentry_revalidate_common(struct dentry *dentry,
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -2058,7 +2058,8 @@ void pid_update_inode(struct task_struct *task, struct inode *inode)
|
|||
* 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 task_struct *task;
|
||||
|
@ -2191,7 +2192,8 @@ static int dname_to_vma_addr(struct dentry *dentry,
|
|||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
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 inode *inode;
|
||||
|
|
|
@ -216,7 +216,8 @@ void proc_free_inum(unsigned int inum)
|
|||
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)
|
||||
return -ECHILD;
|
||||
|
@ -343,7 +344,8 @@ static const struct file_operations proc_dir_operations = {
|
|||
.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;
|
||||
}
|
||||
|
|
|
@ -884,7 +884,8 @@ static const struct inode_operations proc_sys_dir_operations = {
|
|||
.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)
|
||||
return -ECHILD;
|
||||
|
|
|
@ -737,7 +737,8 @@ again:
|
|||
}
|
||||
|
||||
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;
|
||||
int rc;
|
||||
|
|
|
@ -457,7 +457,8 @@ static void tracefs_d_release(struct dentry *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;
|
||||
|
||||
|
|
|
@ -192,7 +192,8 @@ const struct file_operations vboxsf_dir_fops = {
|
|||
* 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.
|
||||
*/
|
||||
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)
|
||||
return -ECHILD;
|
||||
|
|
|
@ -68,16 +68,24 @@ extern const struct qstr dotdot_name;
|
|||
* large memory footprint increase).
|
||||
*/
|
||||
#ifdef CONFIG_64BIT
|
||||
# define DNAME_INLINE_LEN 40 /* 192 bytes */
|
||||
# define DNAME_INLINE_WORDS 5 /* 192 bytes */
|
||||
#else
|
||||
# ifdef CONFIG_SMP
|
||||
# define DNAME_INLINE_LEN 36 /* 128 bytes */
|
||||
# define DNAME_INLINE_WORDS 9 /* 128 bytes */
|
||||
# else
|
||||
# define DNAME_INLINE_LEN 44 /* 128 bytes */
|
||||
# define DNAME_INLINE_WORDS 11 /* 128 bytes */
|
||||
# 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_iname d_shortname.string
|
||||
|
||||
struct dentry {
|
||||
/* RCU lookup touched fields */
|
||||
|
@ -88,7 +96,7 @@ struct dentry {
|
|||
struct qstr d_name;
|
||||
struct inode *d_inode; /* Where the name belongs to - NULL is
|
||||
* negative */
|
||||
unsigned char d_iname[DNAME_INLINE_LEN]; /* small names */
|
||||
union shortname_store d_shortname;
|
||||
/* --- cacheline 1 boundary (64 bytes) was 32 bytes ago --- */
|
||||
|
||||
/* Ref lookup also touches following */
|
||||
|
@ -136,7 +144,8 @@ enum d_real_type {
|
|||
};
|
||||
|
||||
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_hash)(const struct dentry *, struct qstr *);
|
||||
int (*d_compare)(const struct dentry *,
|
||||
|
@ -150,6 +159,8 @@ struct dentry_operations {
|
|||
struct vfsmount *(*d_automount)(struct path *);
|
||||
int (*d_manage)(const struct path *, bool);
|
||||
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;
|
||||
|
||||
/*
|
||||
|
@ -589,7 +600,7 @@ static inline struct inode *d_real_inode(const struct dentry *dentry)
|
|||
|
||||
struct name_snapshot {
|
||||
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 release_dentry_name_snapshot(struct name_snapshot *);
|
||||
|
|
|
@ -192,7 +192,8 @@ struct fscrypt_operations {
|
|||
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 *
|
||||
fscrypt_get_inode_info(const struct inode *inode)
|
||||
|
@ -711,8 +712,8 @@ static inline u64 fscrypt_fname_siphash(const struct inode *dir,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline int fscrypt_d_revalidate(struct dentry *dentry,
|
||||
unsigned int flags)
|
||||
static inline int fscrypt_d_revalidate(struct inode *dir, const struct qstr *name,
|
||||
struct dentry *dentry, unsigned int flags)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -1781,7 +1781,7 @@ struct nfs_rpc_ops {
|
|||
struct nfs_fattr *, struct inode *);
|
||||
int (*setattr) (struct dentry *, struct nfs_fattr *,
|
||||
struct iattr *);
|
||||
int (*lookup) (struct inode *, struct dentry *,
|
||||
int (*lookup) (struct inode *, struct dentry *, const struct qstr *,
|
||||
struct nfs_fh *, struct nfs_fattr *);
|
||||
int (*lookupp) (struct inode *, struct nfs_fh *,
|
||||
struct nfs_fattr *);
|
||||
|
|
|
@ -25,7 +25,7 @@ static long check_vma(struct task_struct *task, struct vm_area_struct *vma,
|
|||
{
|
||||
if (vma->vm_file)
|
||||
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 */
|
||||
if (vma->vm_flags & VM_EXEC)
|
||||
|
|
Loading…
Add table
Reference in a new issue