Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+8 −2
Original line number Diff line number Diff line
@@ -585,6 +585,9 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,

	if (!xent) {
		dent->ch.node_type = UBIFS_DENT_NODE;
		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 {
		dent->ch.node_type = UBIFS_XENT_NODE;
@@ -629,6 +632,9 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
	kfree(dent);

	if (deletion) {
		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)
			goto out_ro;
+107 −22
Original line number Diff line number Diff line
@@ -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);
}

static int do_lookup_dh(struct ubifs_info *c, const union ubifs_key *key,
			struct ubifs_dent_node *dent, uint32_t cookie)
static int search_dh_cookie(struct ubifs_info *c, const union ubifs_key *key,
			    struct ubifs_dent_node *dent, uint32_t cookie,
			    struct ubifs_znode **zn, int *n)
{
	int n, err, type = key_type(c, key);
	struct ubifs_znode *znode;
	int err;
	struct ubifs_znode *znode = *zn;
	struct ubifs_zbranch *zbr;
	union ubifs_key *dkey, 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;
	union ubifs_key *dkey;

	for (;;) {
		if (!err) {
			err = tnc_next(c, &znode, &n);
			err = tnc_next(c, &znode, n);
			if (err)
				goto out_unlock;
				goto out;
		}

		zbr = &znode->zbranch[n];
		zbr = &znode->zbranch[*n];
		dkey = &zbr->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;
			goto out_unlock;
			goto out;
		}

		err = tnc_read_hashed_node(c, zbr, dent);
		if (err)
			goto out_unlock;
			goto out;

		if (key_hash(c, key) == key_hash(c, dkey) &&
		    le32_to_cpu(dent->cookie) == cookie)
			goto out_unlock;
		    le32_to_cpu(dent->cookie) == cookie) {
			*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:
	mutex_unlock(&c->tnc_mutex);
	return err;
@@ -2662,6 +2679,74 @@ int ubifs_tnc_remove_nm(struct ubifs_info *c, const union ubifs_key *key,
	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.
 * @c: UBIFS file-system description object
+2 −0
Original line number Diff line number Diff line
@@ -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_nm(struct ubifs_info *c, const union ubifs_key *key,
			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,
			   union ubifs_key *to_key);
int ubifs_tnc_remove_ino(struct ubifs_info *c, ino_t inum);