mirror of
https://github.com/Dasharo/linux.git
synced 2026-03-06 15:25:10 -08:00
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:
@@ -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;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user