scoutfs: fix binary search for sorted srch block

The search_xattrs ioctl looks for srch entries in srch files that map
the caller's hashed xattr name to inodes.  As it searches it maintains a
range of entries that it is looking for.  When it searches sorted srch
files for entries it first performs a binary search for the start of the
range and then iterates over the blocks until it reaches the end of its
range.

The binary search for the start of the range was a bit wrong.  If the
start of the range was less than all the blocks then the binary search
could wrap the left index, try to get a file block at a negative index,
and return an error for the search.

This is relatively hard to hit in practice.  You have to search for the
xattr name with the smallest hashed value and have a sorted srch file
that's just the right size so that blk offset 0 is the last block
compared in the binary search, which sets the right index to -1.  If
there are lots of xattrs, or sorted files of the wrong length, it'll
work.

This fixes the binary search so that it specifically records the first
block offset that intersects with the range and tests that the left and
right offsets haven't been inverted.  Now that we're not breaking out of
the binary search loop we can more obviously put each block reference
that we get.

Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
Zach Brown
2020-12-03 09:58:35 -08:00
committed by Andy Grover
parent 4647a6ccb2
commit 560c91a0e4
+26 -15
View File
@@ -758,34 +758,45 @@ static int search_sorted_file(struct super_block *sb,
struct scoutfs_block *bl = NULL;
int ret = 0;
int pos = 0;
u64 left;
u64 right;
s64 left;
s64 right;
u64 first;
u64 blk;
/* binary search for the block that contains the start */
blk = 0;
if (sfl->blocks == 0)
return 0;
/* binary search for first block in the range */
first = U64_MAX;
left = 0;
right = le64_to_cpu(sfl->blocks) - 1;
while (left != right) {
while (left <= right) {
blk = (left + right) >> 1;
scoutfs_block_put(sb, bl);
ret = get_file_block(sb, NULL, NULL, sfl, 0, blk, &bl);
if (ret < 0)
goto out;
srb = bl->data;
if (sre_cmp(start, &srb->first) < 0)
right = --blk;
else if (sre_cmp(start, &srb->last) > 0)
left = ++blk;
else
break;
if (sre_cmp(end, &srb->first) < 0) {
right = blk - 1;
} else if (sre_cmp(start, &srb->last) > 0) {
left = blk + 1;
} else {
first = min(blk, first);
right = blk - 1;
}
scoutfs_block_put(sb, bl);
bl = NULL;
}
/* blk is the result of the search */
scoutfs_block_put(sb, bl);
bl = NULL;
/* no blocks in range */
if (first == U64_MAX) {
ret = 0;
goto out;
}
blk = first;
/* stream entries until end or we're past the full tracking rb_root */
for (;;) {