Commit 781f675e authored by Richard Weinberger's avatar Richard Weinberger

ubifs: Fix unlink code wrt. double hash lookups

When removing an encrypted file with a long name and without having
the key we have to be able to locate and remove the directory entry
via a double hash. This corner case was simply forgotten.

Fixes: 528e3d17 ("ubifs: Add full hash lookup support")
Reported-by: default avatarDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>
Signed-off-by: default avatarRichard Weinberger <richard@nod.at>
parent 59a74990
...@@ -585,7 +585,10 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir, ...@@ -585,7 +585,10 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
if (!xent) { if (!xent) {
dent->ch.node_type = UBIFS_DENT_NODE; dent->ch.node_type = UBIFS_DENT_NODE;
dent_key_init(c, &dent_key, dir->i_ino, nm); if (nm->hash)
dent_key_init_hash(c, &dent_key, dir->i_ino, nm->hash);
else
dent_key_init(c, &dent_key, dir->i_ino, nm);
} else { } else {
dent->ch.node_type = UBIFS_XENT_NODE; dent->ch.node_type = UBIFS_XENT_NODE;
xent_key_init(c, &dent_key, dir->i_ino, nm); xent_key_init(c, &dent_key, dir->i_ino, nm);
...@@ -629,7 +632,10 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir, ...@@ -629,7 +632,10 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
kfree(dent); kfree(dent);
if (deletion) { if (deletion) {
err = ubifs_tnc_remove_nm(c, &dent_key, nm); if (nm->hash)
err = ubifs_tnc_remove_dh(c, &dent_key, nm->minor_hash);
else
err = ubifs_tnc_remove_nm(c, &dent_key, nm);
if (err) if (err)
goto out_ro; goto out_ro;
err = ubifs_add_dirt(c, lnum, dlen); err = ubifs_add_dirt(c, lnum, dlen);
......
...@@ -1880,48 +1880,65 @@ int ubifs_tnc_lookup_nm(struct ubifs_info *c, const union ubifs_key *key, ...@@ -1880,48 +1880,65 @@ int ubifs_tnc_lookup_nm(struct ubifs_info *c, const union ubifs_key *key,
return do_lookup_nm(c, key, node, nm); return do_lookup_nm(c, key, node, nm);
} }
static int do_lookup_dh(struct ubifs_info *c, const union ubifs_key *key, static int search_dh_cookie(struct ubifs_info *c, const union ubifs_key *key,
struct ubifs_dent_node *dent, uint32_t cookie) struct ubifs_dent_node *dent, uint32_t cookie,
struct ubifs_znode **zn, int *n)
{ {
int n, err, type = key_type(c, key); int err;
struct ubifs_znode *znode; struct ubifs_znode *znode = *zn;
struct ubifs_zbranch *zbr; struct ubifs_zbranch *zbr;
union ubifs_key *dkey, start_key; union ubifs_key *dkey;
ubifs_assert(is_hash_key(c, key));
lowest_dent_key(c, &start_key, key_inum(c, key));
mutex_lock(&c->tnc_mutex);
err = ubifs_lookup_level0(c, &start_key, &znode, &n);
if (unlikely(err < 0))
goto out_unlock;
for (;;) { for (;;) {
if (!err) { if (!err) {
err = tnc_next(c, &znode, &n); err = tnc_next(c, &znode, n);
if (err) if (err)
goto out_unlock; goto out;
} }
zbr = &znode->zbranch[n]; zbr = &znode->zbranch[*n];
dkey = &zbr->key; dkey = &zbr->key;
if (key_inum(c, dkey) != key_inum(c, key) || if (key_inum(c, dkey) != key_inum(c, key) ||
key_type(c, dkey) != type) { key_type(c, dkey) != key_type(c, key)) {
err = -ENOENT; err = -ENOENT;
goto out_unlock; goto out;
} }
err = tnc_read_hashed_node(c, zbr, dent); err = tnc_read_hashed_node(c, zbr, dent);
if (err) if (err)
goto out_unlock; goto out;
if (key_hash(c, key) == key_hash(c, dkey) && if (key_hash(c, key) == key_hash(c, dkey) &&
le32_to_cpu(dent->cookie) == cookie) le32_to_cpu(dent->cookie) == cookie) {
goto out_unlock; *zn = znode;
goto out;
}
} }
out:
return err;
}
static int do_lookup_dh(struct ubifs_info *c, const union ubifs_key *key,
struct ubifs_dent_node *dent, uint32_t cookie)
{
int n, err;
struct ubifs_znode *znode;
union ubifs_key start_key;
ubifs_assert(is_hash_key(c, key));
lowest_dent_key(c, &start_key, key_inum(c, key));
mutex_lock(&c->tnc_mutex);
err = ubifs_lookup_level0(c, &start_key, &znode, &n);
if (unlikely(err < 0))
goto out_unlock;
err = search_dh_cookie(c, key, dent, cookie, &znode, &n);
out_unlock: out_unlock:
mutex_unlock(&c->tnc_mutex); mutex_unlock(&c->tnc_mutex);
return err; return err;
...@@ -2662,6 +2679,74 @@ int ubifs_tnc_remove_nm(struct ubifs_info *c, const union ubifs_key *key, ...@@ -2662,6 +2679,74 @@ int ubifs_tnc_remove_nm(struct ubifs_info *c, const union ubifs_key *key,
return err; return err;
} }
/**
* ubifs_tnc_remove_dh - remove an index entry for a "double hashed" node.
* @c: UBIFS file-system description object
* @key: key of node
* @cookie: node cookie for collision resolution
*
* Returns %0 on success or negative error code on failure.
*/
int ubifs_tnc_remove_dh(struct ubifs_info *c, const union ubifs_key *key,
uint32_t cookie)
{
int n, err;
struct ubifs_znode *znode;
struct ubifs_dent_node *dent;
struct ubifs_zbranch *zbr;
if (!c->double_hash)
return -EOPNOTSUPP;
mutex_lock(&c->tnc_mutex);
err = lookup_level0_dirty(c, key, &znode, &n);
if (err <= 0)
goto out_unlock;
zbr = &znode->zbranch[n];
dent = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS);
if (!dent) {
err = -ENOMEM;
goto out_unlock;
}
err = tnc_read_hashed_node(c, zbr, dent);
if (err)
goto out_free;
/* If the cookie does not match, we're facing a hash collision. */
if (le32_to_cpu(dent->cookie) != cookie) {
union ubifs_key start_key;
lowest_dent_key(c, &start_key, key_inum(c, key));
err = ubifs_lookup_level0(c, &start_key, &znode, &n);
if (unlikely(err < 0))
goto out_free;
err = search_dh_cookie(c, key, dent, cookie, &znode, &n);
if (err)
goto out_free;
}
if (znode->cnext || !ubifs_zn_dirty(znode)) {
znode = dirty_cow_bottom_up(c, znode);
if (IS_ERR(znode)) {
err = PTR_ERR(znode);
goto out_free;
}
}
err = tnc_delete(c, znode, n);
out_free:
kfree(dent);
out_unlock:
if (!err)
err = dbg_check_tnc(c, 0);
mutex_unlock(&c->tnc_mutex);
return err;
}
/** /**
* key_in_range - determine if a key falls within a range of keys. * key_in_range - determine if a key falls within a range of keys.
* @c: UBIFS file-system description object * @c: UBIFS file-system description object
......
...@@ -1589,6 +1589,8 @@ int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key, ...@@ -1589,6 +1589,8 @@ int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key,
int ubifs_tnc_remove(struct ubifs_info *c, const union ubifs_key *key); int ubifs_tnc_remove(struct ubifs_info *c, const union ubifs_key *key);
int ubifs_tnc_remove_nm(struct ubifs_info *c, const union ubifs_key *key, int ubifs_tnc_remove_nm(struct ubifs_info *c, const union ubifs_key *key,
const struct fscrypt_name *nm); const struct fscrypt_name *nm);
int ubifs_tnc_remove_dh(struct ubifs_info *c, const union ubifs_key *key,
uint32_t cookie);
int ubifs_tnc_remove_range(struct ubifs_info *c, union ubifs_key *from_key, int ubifs_tnc_remove_range(struct ubifs_info *c, union ubifs_key *from_key,
union ubifs_key *to_key); union ubifs_key *to_key);
int ubifs_tnc_remove_ino(struct ubifs_info *c, ino_t inum); int ubifs_tnc_remove_ino(struct ubifs_info *c, ino_t inum);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment