mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Viacheslav Dubeyko <vdubeyko@redhat.com>
To: Shardul Bankar <shardul.b@mpiricsoftware.com>,
	slava@dubeyko.com,  glaubitz@physik.fu-berlin.de,
	frank.li@vivo.com
Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org,
	 janak@mpiric.us, kalpan.jani@mpiricsoftware.com,
	shardulsb08@gmail.com
Subject: Re: [PATCH] hfsplus: fix hfs_bnode_split() failure on sparsely-filled nodes
Date: Fri, 24 Apr 2026 13:06:05 -0700	[thread overview]
Message-ID: <d6c14c73839aa899b4fa40a04e8d650c3c2c49f8.camel@redhat.com> (raw)
In-Reply-To: <20260423214936.444718-1-shardul.b@mpiricsoftware.com>

On Fri, 2026-04-24 at 03:19 +0530, Shardul Bankar wrote:
> hfs_bnode_split() determines the split point by scanning the node's
> offset table for the first record whose data offset exceeds a threshold
> derived from node_size / 2. When all record data fits within the first
> half of the node, no record offset exceeds the threshold, the loop
> exhausts all records, and the function returns -ENOSPC even though the
> node can be validly split. This causes xattr insertions to fail
> silently.
> 
> The failing code path is exercised by xfstests generic/070 and
> generic/642 during xattr stress operations.
> 
> Fix this by re-scanning with a threshold based on the actual data
> midpoint when the position-based scan exhausts. If the re-scan also
> exhausts, fall back to splitting off the last record.
> 
> Reported-by: Viacheslav Dubeyko <slava@dubeyko.com>
> Signed-off-by: Shardul Bankar <shardul.b@mpiricsoftware.com>
> ---
>  fs/hfsplus/brec.c | 32 ++++++++++++++++++++++++++------
>  1 file changed, 26 insertions(+), 6 deletions(-)
> 
> diff --git a/fs/hfsplus/brec.c b/fs/hfsplus/brec.c
> index e3df89284079..cfc909c808a4 100644
> --- a/fs/hfsplus/brec.c
> +++ b/fs/hfsplus/brec.c
> @@ -282,12 +282,32 @@ static struct hfs_bnode *hfs_bnode_split(struct hfs_find_data *fd)
>  		old_rec_off -= rec_size;
>  		if (++num_recs < node->num_recs)
>  			continue;
> -		hfs_bnode_put(node);
> -		hfs_bnode_unlink(new_node);
> -		hfs_bnode_put(new_node);
> -		if (next_node)
> -			hfs_bnode_put(next_node);
> -		return ERR_PTR(-ENOSPC);

I don't quite follow why do we completely remove this logic? Potentially, it
could be valid situation. No?

Also, I think we need to add hfs_bnode_unlink(new_node) here [1]. Otherwise,
hfs_bnode_put() will be unable to destroy the node.

> +		/*
> +		 * All data fits within the node_size/2 threshold,
> +		 * so re-scan using the actual data midpoint.
> +		 */

First of all, I don't see the answer on question. Why we call this method for
node that is not completely full (or near to be full). If we have half empty
node, then, what's the point to execute node splitting? We have enough space to
add more items. Do we have the same issue for Catalog File and Extents file?

> +		size = hfs_bnode_read_u16(node, tree->node_size -
> +			(node->num_recs + 1) * rec_size);

I prefer to executes likewise calculation more carefully. We could have
incorrect tree->node_size, weird node->num_recs value.

Why do we need node->num_recs + 1?

Potentially, we could have overflow here.

> +		size = ((int)node_desc_size + size) / 2;

How can we blindly rely on size that we received from hfs_bnode_read_u16()? What
if it was really huge or really small value?

> +		old_rec_off = tree->node_size - (2 * rec_size);

Why 2 was hardcoded here? What this value mean? Why do we need to multiple
rec_size on 2?

> +		num_recs = 1;
> +		for (;;) {

I slightly dislike infinite loop here. Why do we need to use infinite loop? We
have size or number of items estimation. What the point to use infinite loop? It
is always bad coding style.

> +			data_start = hfs_bnode_read_u16(node,
> +							old_rec_off);
> +			if (data_start > size)
> +				break;
> +			old_rec_off -= rec_size;
> +			if (++num_recs < node->num_recs)

This is can be used as for loop boundary. Why infinite loop has been used?

> +				continue;
> +			/* last record holds most of the data */
> +			num_recs = node->num_recs - 1;
> +			old_rec_off = tree->node_size -
> +				(num_recs + 1) * rec_size;

The same concerns related to calculation here.

Thanks,
Slava.

> +			data_start = hfs_bnode_read_u16(node,
> +							old_rec_off);
> +			break;
> +		}
> +		break;
>  	}
>  
>  	if (fd->record + 1 < num_recs) {

[1] https://elixir.bootlin.com/linux/v7.0/source/fs/hfsplus/brec.c#L262


      reply	other threads:[~2026-04-24 20:06 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-23 21:49 Shardul Bankar
2026-04-24 20:06 ` Viacheslav Dubeyko [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=d6c14c73839aa899b4fa40a04e8d650c3c2c49f8.camel@redhat.com \
    --to=vdubeyko@redhat.com \
    --cc=frank.li@vivo.com \
    --cc=glaubitz@physik.fu-berlin.de \
    --cc=janak@mpiric.us \
    --cc=kalpan.jani@mpiricsoftware.com \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=shardul.b@mpiricsoftware.com \
    --cc=shardulsb08@gmail.com \
    --cc=slava@dubeyko.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®