@@ -108,22 +108,39 @@ const RECOVERY_WATERMARK_KEY: u64 = 2;
108108
109109/// Return the first retained logical position in `section`.
110110#[ inline]
111- const fn first_in_section ( pruning_boundary : u64 , section : u64 , items_per_blob : u64 ) -> u64 {
112- let start = section * items_per_blob;
111+ fn first_in_section (
112+ pruning_boundary : u64 ,
113+ section : u64 ,
114+ items_per_blob : u64 ,
115+ ) -> Result < u64 , Error > {
116+ let start = section
117+ . checked_mul ( items_per_blob)
118+ . ok_or ( Error :: OffsetOverflow ) ?;
113119 if pruning_boundary > start {
114- pruning_boundary
120+ Ok ( pruning_boundary)
115121 } else {
116- start
122+ Ok ( start)
117123 }
118124}
119125
120126/// Maximum number of items a section's blob can physically hold. This is `items_per_blob` unless
121127/// the pruning boundary falls mid-section (from `init_at_size`), in which case the skipped prefix
122128/// reduces the capacity.
123129#[ inline]
124- const fn section_capacity ( pruning_boundary : u64 , section : u64 , items_per_blob : u64 ) -> u64 {
125- let start = section * items_per_blob;
126- items_per_blob - ( first_in_section ( pruning_boundary, section, items_per_blob) - start)
130+ fn section_capacity (
131+ pruning_boundary : u64 ,
132+ section : u64 ,
133+ items_per_blob : u64 ,
134+ ) -> Result < u64 , Error > {
135+ let start = section
136+ . checked_mul ( items_per_blob)
137+ . ok_or ( Error :: OffsetOverflow ) ?;
138+ let skipped = first_in_section ( pruning_boundary, section, items_per_blob) ?
139+ . checked_sub ( start)
140+ . ok_or ( Error :: OffsetOverflow ) ?;
141+ items_per_blob
142+ . checked_sub ( skipped)
143+ . ok_or ( Error :: OffsetOverflow )
127144}
128145
129146/// Configuration for `Journal` storage.
@@ -196,7 +213,8 @@ impl<E: Context, A: CodecFixedShared> Inner<E, A> {
196213 }
197214
198215 let section = pos / items_per_blob;
199- let pos_in_section = pos - first_in_section ( self . pruning_boundary , section, items_per_blob) ;
216+ let pos_in_section =
217+ pos - first_in_section ( self . pruning_boundary , section, items_per_blob) ?;
200218
201219 self . journal
202220 . get ( section, pos_in_section)
@@ -226,7 +244,8 @@ impl<E: Context, A: CodecFixedShared> Inner<E, A> {
226244 return None ;
227245 }
228246 let section = pos / items_per_blob;
229- let pos_in_section = pos - first_in_section ( self . pruning_boundary , section, items_per_blob) ;
247+ let pos_in_section =
248+ pos - first_in_section ( self . pruning_boundary , section, items_per_blob) . ok ( ) ?;
230249 self . journal . try_get_sync_into ( section, pos_in_section, buf)
231250 }
232251}
@@ -355,7 +374,7 @@ impl<E: Context, A: CodecFixedShared> super::Reader for Reader<'_, E, A> {
355374 }
356375
357376 let group_len = group_end - group_start;
358- let first_position = first_in_section ( pruning_boundary, section, items_per_blob) ;
377+ let first_position = first_in_section ( pruning_boundary, section, items_per_blob) ? ;
359378 let section_positions: Vec < u64 > = miss_positions[ group_start..group_end]
360379 . iter ( )
361380 . map ( |& pos| pos - first_position)
@@ -423,7 +442,7 @@ impl<E: Context, A: CodecFixedShared> super::Reader for Reader<'_, E, A> {
423442
424443 let start_section = start_pos / items_per_blob;
425444 let start_pos_in_section =
426- start_pos - first_in_section ( pruning_boundary, start_section, items_per_blob) ;
445+ start_pos - first_in_section ( pruning_boundary, start_section, items_per_blob) ? ;
427446
428447 // Check all middle sections (not oldest, not tail) in range are complete.
429448 let journal = & self . guard . journal ;
@@ -445,10 +464,11 @@ impl<E: Context, A: CodecFixedShared> super::Reader for Reader<'_, E, A> {
445464
446465 // Transform (section, pos_in_section, item) to (global_pos, item).
447466 let stream = inner_stream. map ( move |result| {
448- result. map ( |( section, pos_in_section, item) | {
449- let global_pos =
450- first_in_section ( pruning_boundary, section, items_per_blob) + pos_in_section;
451- ( global_pos, item)
467+ result. and_then ( |( section, pos_in_section, item) | {
468+ let global_pos = first_in_section ( pruning_boundary, section, items_per_blob) ?
469+ . checked_add ( pos_in_section)
470+ . ok_or ( Error :: OffsetOverflow ) ?;
471+ Ok ( ( global_pos, item) )
452472 } )
453473 } ) ;
454474
@@ -696,7 +716,12 @@ impl<E: Context, A: CodecFixedShared> Journal<E, A> {
696716 meta_pruning_boundary : Option < u64 > ,
697717 meta_recovery_watermark : Option < u64 > ,
698718 ) -> Result < ( u64 , u64 , u64 , Option < RecoveryRepair > ) , Error > {
699- let blob_boundary = inner. oldest_section ( ) . map_or ( 0 , |o| o * items_per_blob) ;
719+ let blob_boundary = match inner. oldest_section ( ) {
720+ Some ( oldest) => oldest
721+ . checked_mul ( items_per_blob)
722+ . ok_or ( Error :: OffsetOverflow ) ?,
723+ None => 0 ,
724+ } ;
700725
701726 // Determine the pruning boundary from metadata and blob state.
702727 //
@@ -781,7 +806,7 @@ impl<E: Context, A: CodecFixedShared> Journal<E, A> {
781806 } ;
782807
783808 let oldest_len = inner. section_len ( oldest) . await ?;
784- let expected = section_capacity ( pruning_boundary, oldest, items_per_blob) ;
809+ let expected = section_capacity ( pruning_boundary, oldest, items_per_blob) ? ;
785810
786811 if oldest_len > expected {
787812 return Err ( Error :: Corruption ( format ! (
@@ -799,7 +824,7 @@ impl<E: Context, A: CodecFixedShared> Journal<E, A> {
799824 section : u64 ,
800825 ) -> Result < ( u64 , u64 ) , Error > {
801826 let len = inner. section_len ( section) . await ?;
802- let capacity = section_capacity ( pruning_boundary, section, items_per_blob) ;
827+ let capacity = section_capacity ( pruning_boundary, section, items_per_blob) ? ;
803828 if len > capacity {
804829 return Err ( Error :: Corruption ( format ! (
805830 "section {section} has too many items: expected at most {capacity}, got {len}"
@@ -838,7 +863,9 @@ impl<E: Context, A: CodecFixedShared> Journal<E, A> {
838863 let ( tail_len, _) =
839864 Self :: section_len_within_capacity ( inner, items_per_blob, pruning_boundary, newest)
840865 . await ?;
841- let size = first_in_section ( pruning_boundary, newest, items_per_blob) + tail_len;
866+ let size = first_in_section ( pruning_boundary, newest, items_per_blob) ?
867+ . checked_add ( tail_len)
868+ . ok_or ( Error :: OffsetOverflow ) ?;
842869 Ok ( ( size, None ) )
843870 }
844871
@@ -860,8 +887,10 @@ impl<E: Context, A: CodecFixedShared> Journal<E, A> {
860887
861888 // The oldest section's capacity was already checked before recovery mode dispatch.
862889 let oldest_len = inner. section_len ( oldest) . await ?;
863- let expected_oldest = section_capacity ( pruning_boundary, oldest, items_per_blob) ;
864- let mut size = pruning_boundary + oldest_len;
890+ let expected_oldest = section_capacity ( pruning_boundary, oldest, items_per_blob) ?;
891+ let mut size = pruning_boundary
892+ . checked_add ( oldest_len)
893+ . ok_or ( Error :: OffsetOverflow ) ?;
865894
866895 if oldest == newest {
867896 return Ok ( ( size, None ) ) ;
@@ -872,7 +901,9 @@ impl<E: Context, A: CodecFixedShared> Journal<E, A> {
872901 size,
873902 Some ( RecoveryRepair {
874903 section : oldest,
875- byte_offset : oldest_len * Self :: CHUNK_SIZE_U64 ,
904+ byte_offset : oldest_len
905+ . checked_mul ( Self :: CHUNK_SIZE_U64 )
906+ . ok_or ( Error :: OffsetOverflow ) ?,
876907 } ) ,
877908 ) ) ;
878909 }
@@ -882,7 +913,7 @@ impl<E: Context, A: CodecFixedShared> Journal<E, A> {
882913 Self :: section_len_within_capacity ( inner, items_per_blob, pruning_boundary, section)
883914 . await ?;
884915
885- size += len;
916+ size = size . checked_add ( len) . ok_or ( Error :: OffsetOverflow ) ? ;
886917 if len < capacity {
887918 if section == newest {
888919 return Ok ( ( size, None ) ) ;
@@ -891,7 +922,9 @@ impl<E: Context, A: CodecFixedShared> Journal<E, A> {
891922 size,
892923 Some ( RecoveryRepair {
893924 section,
894- byte_offset : len * Self :: CHUNK_SIZE_U64 ,
925+ byte_offset : len
926+ . checked_mul ( Self :: CHUNK_SIZE_U64 )
927+ . ok_or ( Error :: OffsetOverflow ) ?,
895928 } ) ,
896929 ) ) ;
897930 }
@@ -1164,8 +1197,10 @@ impl<E: Context, A: CodecFixedShared> Journal<E, A> {
11641197
11651198 let section = size / self . items_per_blob ;
11661199 let pos_in_section =
1167- size - first_in_section ( inner. pruning_boundary , section, self . items_per_blob ) ;
1168- let byte_offset = pos_in_section * Self :: CHUNK_SIZE_U64 ;
1200+ size - first_in_section ( inner. pruning_boundary , section, self . items_per_blob ) ?;
1201+ let byte_offset = pos_in_section
1202+ . checked_mul ( Self :: CHUNK_SIZE_U64 )
1203+ . ok_or ( Error :: OffsetOverflow ) ?;
11691204
11701205 let should_sync_metadata = Self :: lower_recovery_watermark ( & mut inner, size) ?;
11711206 drop ( inner) ;
0 commit comments