Skip to content

Commit 4220ff7

Browse files
committed
dnode_next_offset: clamp *offset for limit cases
And add documentation to explain the calculation Signed-off-by: Robert Evans <[email protected]>
1 parent 6b27138 commit 4220ff7

File tree

1 file changed

+33
-9
lines changed

1 file changed

+33
-9
lines changed

module/zfs/dnode.c

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2727,18 +2727,42 @@ dnode_next_offset(dnode_t *dn, int flags, uint64_t *offset,
27272727
}
27282728

27292729
if (lvl > 0) {
2730-
uint64_t n = blkid << epbs;
2731-
if (index > 0 || n > 0)
2732-
n += index; /* -1 <= index <= 1<<epbs */
2733-
27342730
int span = (lvl - 1) * epbs + dn->dn_datablkshift;
2735-
if (span >= 8 * sizeof (uint64_t))
2736-
*offset = 0;
2737-
else if (flags & DNODE_FIND_BACKWARDS)
2738-
/* traversing backwards; position at block end */
2731+
uint64_t n;
2732+
2733+
/*
2734+
* Calculate the range of L0 offsets for blkid n at lvl - 1.
2735+
* Forward search: Return the first offset in the range or
2736+
* the initial offset, whichever is larger.
2737+
* Backward search: Return the last offset (inclusive) or the
2738+
* initial offset, whichever is smaller.
2739+
*/
2740+
n = (blkid << epbs) + index; /* -1 <= index <= (1<<epbs) */
2741+
if (index < 0 && blkid == 0) {
2742+
*offset = 0; /* Not found searching backwards */
2743+
} else if (span >= 8 * sizeof (uint64_t) ||
2744+
(n >> (8 * sizeof (*offset) - span)) != 0) {
2745+
/*
2746+
* Overflow cases:
2747+
* n == 0: range starts at 0 and ends beyond UINT64_MAX
2748+
* n > 0: range starts (and ends) beyond UINT64_MAX
2749+
*
2750+
* This leaves *offset unchanged in two cases:
2751+
* 1. Searching forward, n == 0: the range starts at 0
2752+
* which is always <= any initial offset.
2753+
* 2. Searching backward, any n: the range ends beyond
2754+
* UINT64_MAX which is always >= any initial offset.
2755+
*
2756+
* Searching forward, n > 0: the range start overflows,
2757+
* so clamp *offset to UINT64_MAX upon return.
2758+
*/
2759+
if (n > 0 && !(flags & DNODE_FIND_BACKWARDS))
2760+
*offset = UINT64_MAX; /* Forward overflow */
2761+
} else if (flags & DNODE_FIND_BACKWARDS) {
27392762
*offset = MIN(*offset, ((n + 1) << span) - 1);
2740-
else
2763+
} else {
27412764
*offset = MAX(*offset, n << span);
2765+
}
27422766
} else {
27432767
*offset = (blkid << dn->dn_datablkshift) +
27442768
(index << DNODE_SHIFT); /* 0 <= index <= blkfill */

0 commit comments

Comments
 (0)