ceph: avoid d_parent in ceph_dentry_hash; fix ceph_encode_fh() hashing bug

Have caller pass in a safely-obtained reference to the parent directory
for calculating a dentry's hash valud.

While we're here, simpify the flow through ceph_encode_fh() so that there
is a single exit point and cleanup.

Also fix a bug with the dentry hash calculation: calculate the hash for the
dentry we were given, not its parent.

Reviewed-by: Yehuda Sadeh <yehuda@hq.newdream.net>
Signed-off-by: Sage Weil <sage@newdream.net>
This commit is contained in:
Sage Weil 2011-07-26 11:30:55 -07:00
parent bf1c6aca96
commit e5f86dc377
4 changed files with 18 additions and 13 deletions

View file

@ -1244,9 +1244,8 @@ void ceph_dentry_lru_del(struct dentry *dn)
* Return name hash for a given dentry. This is dependent on * Return name hash for a given dentry. This is dependent on
* the parent directory's hash function. * the parent directory's hash function.
*/ */
unsigned ceph_dentry_hash(struct dentry *dn) unsigned ceph_dentry_hash(struct inode *dir, struct dentry *dn)
{ {
struct inode *dir = dn->d_parent->d_inode;
struct ceph_inode_info *dci = ceph_inode(dir); struct ceph_inode_info *dci = ceph_inode(dir);
switch (dci->i_dir_layout.dl_dir_hash) { switch (dci->i_dir_layout.dl_dir_hash) {

View file

@ -46,7 +46,7 @@ static int ceph_encode_fh(struct dentry *dentry, u32 *rawfh, int *max_len,
int type; int type;
struct ceph_nfs_fh *fh = (void *)rawfh; struct ceph_nfs_fh *fh = (void *)rawfh;
struct ceph_nfs_confh *cfh = (void *)rawfh; struct ceph_nfs_confh *cfh = (void *)rawfh;
struct dentry *parent = dentry->d_parent; struct dentry *parent;
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
int connected_handle_length = sizeof(*cfh)/4; int connected_handle_length = sizeof(*cfh)/4;
int handle_length = sizeof(*fh)/4; int handle_length = sizeof(*fh)/4;
@ -55,26 +55,33 @@ static int ceph_encode_fh(struct dentry *dentry, u32 *rawfh, int *max_len,
if (ceph_snap(inode) != CEPH_NOSNAP) if (ceph_snap(inode) != CEPH_NOSNAP)
return -EINVAL; return -EINVAL;
spin_lock(&dentry->d_lock);
parent = dget(dentry->d_parent);
spin_unlock(&dentry->d_lock);
if (*max_len >= connected_handle_length) { if (*max_len >= connected_handle_length) {
dout("encode_fh %p connectable\n", dentry); dout("encode_fh %p connectable\n", dentry);
cfh->ino = ceph_ino(dentry->d_inode); cfh->ino = ceph_ino(dentry->d_inode);
cfh->parent_ino = ceph_ino(parent->d_inode); cfh->parent_ino = ceph_ino(parent->d_inode);
cfh->parent_name_hash = ceph_dentry_hash(parent); cfh->parent_name_hash = ceph_dentry_hash(parent->d_inode,
dentry);
*max_len = connected_handle_length; *max_len = connected_handle_length;
type = 2; type = 2;
} else if (*max_len >= handle_length) { } else if (*max_len >= handle_length) {
if (connectable) { if (connectable) {
*max_len = connected_handle_length; *max_len = connected_handle_length;
return 255; type = 255;
} else {
dout("encode_fh %p\n", dentry);
fh->ino = ceph_ino(dentry->d_inode);
*max_len = handle_length;
type = 1;
} }
dout("encode_fh %p\n", dentry);
fh->ino = ceph_ino(dentry->d_inode);
*max_len = handle_length;
type = 1;
} else { } else {
*max_len = handle_length; *max_len = handle_length;
return 255; type = 255;
} }
dput(parent);
return type; return type;
} }
@ -123,7 +130,6 @@ static struct dentry *__fh_to_dentry(struct super_block *sb,
return dentry; return dentry;
} }
err = ceph_init_dentry(dentry); err = ceph_init_dentry(dentry);
if (err < 0) { if (err < 0) {
iput(inode); iput(inode);
return ERR_PTR(err); return ERR_PTR(err);

View file

@ -670,7 +670,7 @@ static int __choose_mds(struct ceph_mds_client *mdsc,
} else { } else {
/* dir + name */ /* dir + name */
inode = dir; inode = dir;
hash = ceph_dentry_hash(req->r_dentry); hash = ceph_dentry_hash(dir, req->r_dentry);
is_hash = true; is_hash = true;
} }
} }

View file

@ -800,7 +800,7 @@ extern void ceph_dentry_lru_add(struct dentry *dn);
extern void ceph_dentry_lru_touch(struct dentry *dn); extern void ceph_dentry_lru_touch(struct dentry *dn);
extern void ceph_dentry_lru_del(struct dentry *dn); extern void ceph_dentry_lru_del(struct dentry *dn);
extern void ceph_invalidate_dentry_lease(struct dentry *dentry); extern void ceph_invalidate_dentry_lease(struct dentry *dentry);
extern unsigned ceph_dentry_hash(struct dentry *dn); extern unsigned ceph_dentry_hash(struct inode *dir, struct dentry *dn);
extern struct inode *ceph_get_dentry_parent_inode(struct dentry *dentry); extern struct inode *ceph_get_dentry_parent_inode(struct dentry *dentry);
/* /*