Commit 18b07c44 authored by Viacheslav Dubeyko's avatar Viacheslav Dubeyko
Browse files

hfs: clear offset and space out of valid records in b-tree node



Currently, hfs_brec_remove() executes moving records
towards the location of deleted record and it updates
offsets of moved records. However, the hfs_brec_remove()
logic ignores the "mess" of b-tree node's free space and
it doesn't touch the offsets out of records number.
Potentially, it could confuse fsck or driver logic or
to be a reason of potential corruption cases.

This patch reworks the logic of hfs_brec_remove()
by means of clearing freed space of b-tree node
after the records moving. And it clear the last
offset that keeping old location of free space
because now the offset before this one is keeping
the actual offset to the free space after the record
deletion.

Signed-off-by: default avatarViacheslav Dubeyko <slava@dubeyko.com>
cc: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
cc: Yangtao Li <frank.li@vivo.com>
cc: linux-fsdevel@vger.kernel.org
Link: https://lore.kernel.org/r/20250815194918.38165-1-slava@dubeyko.com


Signed-off-by: default avatarViacheslav Dubeyko <slava@dubeyko.com>
parent a06ec283
Loading
Loading
Loading
Loading
+23 −4
Original line number Diff line number Diff line
@@ -179,6 +179,7 @@ int hfs_brec_remove(struct hfs_find_data *fd)
	struct hfs_btree *tree;
	struct hfs_bnode *node, *parent;
	int end_off, rec_off, data_off, size;
	int src, dst, len;

	tree = fd->tree;
	node = fd->bnode;
@@ -208,10 +209,14 @@ int hfs_brec_remove(struct hfs_find_data *fd)
	}
	hfs_bnode_write_u16(node, offsetof(struct hfs_bnode_desc, num_recs), node->num_recs);

	if (rec_off == end_off)
		goto skip;
	size = fd->keylength + fd->entrylength;

	if (rec_off == end_off) {
		src = fd->keyoffset;
		hfs_bnode_clear(node, src, size);
		goto skip;
	}

	do {
		data_off = hfs_bnode_read_u16(node, rec_off);
		hfs_bnode_write_u16(node, rec_off + 2, data_off - size);
@@ -219,9 +224,23 @@ int hfs_brec_remove(struct hfs_find_data *fd)
	} while (rec_off >= end_off);

	/* fill hole */
	hfs_bnode_move(node, fd->keyoffset, fd->keyoffset + size,
		       data_off - fd->keyoffset - size);
	dst = fd->keyoffset;
	src = fd->keyoffset + size;
	len = data_off - src;

	hfs_bnode_move(node, dst, src, len);

	src = dst + len;
	len = data_off - src;

	hfs_bnode_clear(node, src, len);

skip:
	/*
	 * Remove the obsolete offset to free space.
	 */
	hfs_bnode_write_u16(node, end_off, 0);

	hfs_bnode_dump(node);
	if (!fd->record)
		hfs_brec_update_parent(fd);