@@ -798,8 +798,11 @@ impl<B: Blob> Append<B> {
798798 ///
799799 /// Since larger valid lengths are authoritative, a shorter CRC cannot simply be written next to
800800 /// the old CRC. We first stage the shorter slot with length 0, then make its length durable,
801- /// then invalidate the old longer slot. A crash during any phase recovers either the old longer
802- /// page or the new shorter page, but never loses the whole page or fabricates a larger length.
801+ /// then invalidate the old longer slot by clearing its CRC bytes. Once that invalidation is
802+ /// durable, the old slot cannot validate under the CRC model used throughout this layer, even
803+ /// though its length bytes remain present. A crash during any phase recovers either the old
804+ /// longer page or the new shorter page, but never loses the whole page or fabricates a larger
805+ /// length.
803806 async fn sync_partial_page_shrink (
804807 blob : & B ,
805808 page : u64 ,
@@ -840,17 +843,17 @@ impl<B: Blob> Append<B> {
840843 } ;
841844 let len_size = std:: mem:: size_of :: < u16 > ( ) ;
842845 let crc_size = CHECKSUM_SLOT_SIZE - len_size;
846+
847+ // Clear CRC bytes before length bytes. If this write is torn, recovery either sees the old
848+ // valid CRC or an invalid old slot. After the following sync, the new shorter slot is the
849+ // only valid slot, so zeroing the old length is unnecessary.
843850 blob. write_at (
844851 crc_start + old_slot_start as u64 + len_size as u64 ,
845852 vec ! [ 0u8 ; crc_size] ,
846853 )
847854 . await ?;
848855 blob. sync ( ) . await ?;
849856
850- blob. write_at ( crc_start + old_slot_start as u64 , vec ! [ 0u8 ; len_size] )
851- . await ?;
852- blob. sync ( ) . await ?;
853-
854857 let final_record = if new_slot_start == 0 {
855858 Checksum {
856859 len1 : new_len,
0 commit comments