xref: /freebsd/sys/contrib/openzfs/cmd/zstream/zstream_fletcher4.c (revision d0b3ecdc274930e190ea233b6b69ff03782eaf8d)
1 // SPDX-License-Identifier: CDDL-1.0
2 /*
3  * CDDL HEADER START
4  *
5  * This file and its contents are supplied under the terms of the Common
6  * Development and Distribution License ("CDDL"), version 1.0. You may only use
7  * this file in accordance with the terms of version 1.0 of the CDDL.
8  *
9  * A full copy of the text of the CDDL should have accompanied this source. A
10  * copy of the CDDL is also available via the Internet at
11  * http://www.illumos.org/license/CDDL.
12  *
13  * CDDL HEADER END
14  */
15 
16 /*
17  * Copyright (c) 2026 by Garth Snyder. All rights reserved.
18  */
19 
20 #include <assert.h>
21 #include <stddef.h>
22 #include <stdint.h>
23 #include <sys/byteorder.h>
24 #include <sys/spa_checksum.h>
25 #include <sys/stdtypes.h>
26 #include <sys/types.h>
27 #include <sys/zfs_ioctl.h>
28 #include <zfs_fletcher.h>
29 
30 #include "zstream_modules.h"
31 #include "zstream_util.h"
32 
33 #define	CK_OFFSET offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum)
34 #define	END_CK_OFFSET offsetof(dmu_replay_record_t, drr_u.drr_end.drr_checksum)
35 
36 typedef enum { F4_SET, F4_VALIDATE } fletcher4_op_t;
37 
38 typedef zio_cksum_t fletcher4_context_t;
39 
40 static fletcher4_context_t	fletcher4_contexts[MAX_FLETCHER_4];
41 static int			next_context = 0;
42 
43 static inline int
fletcher_4_incremental(boolean_t swap,void * buff,size_t size,void * cksum)44 fletcher_4_incremental(boolean_t swap, void *buff, size_t size, void *cksum)
45 {
46 	if (swap) {
47 		return (fletcher_4_incremental_byteswap(buff, size, cksum));
48 	} else {
49 		return (fletcher_4_incremental_native(buff, size, cksum));
50 	}
51 }
52 
53 /*
54  * Emit or validate (below) a replay record with proper checksums and with
55  * proper maintenance of the stream checksum. That is:
56  *
57  *   1) Update stream checksum with the record header up to drr_checksum.
58  *   2) Update drr_checksum field in the record header from stream checksum.
59  *   3) Update stream checksum with the checksum field in the record header.
60  *   4) Update stream checksum with the contents of the payload.
61  *
62  * DRR_BEGIN records do not have record checksums. They can't, because the
63  * drr_begin struct overlaps with space that would otherwise be used for the
64  * end-record checksum.
65  */
66 static disposition_t
chain_add_fletcher4(drr_packet_t * item,zio_cksum_t * stream_cksum)67 chain_add_fletcher4(drr_packet_t *item, zio_cksum_t *stream_cksum)
68 {
69 	if (item == NULL)
70 		return (D_OK);
71 
72 	dmu_replay_record_t *drr   = &item->dp_drr;
73 	struct drr_end *drre	   = &item->dp_drr.drr_u.drr_end;
74 	zio_cksum_t *record_cksum  = &drr->drr_u.drr_checksum.drr_checksum;
75 	zio_cksum_t *end_cksum	   = &drre->drr_checksum;
76 
77 	boolean_t swap = OPTION_ENABLED(CA_BYTESWAP_ON_OUTPUT);
78 	uint32_t drr_type = swap ? BSWAP_32(drr->drr_type) : drr->drr_type;
79 
80 	if (drr_type == DRR_BEGIN) {
81 		ZIO_SET_CHECKSUM(stream_cksum, 0, 0, 0, 0);
82 	} else if (drr_type == DRR_END) {
83 		*end_cksum = *stream_cksum;
84 		if (swap)
85 			ZIO_CHECKSUM_BSWAP(end_cksum);
86 	}
87 	fletcher_4_incremental(swap, drr, CK_OFFSET, stream_cksum);
88 	if (drr_type != DRR_BEGIN && !IS_CONCLUSION(drr, drr_type)) {
89 		*record_cksum = *stream_cksum;
90 		if (swap)
91 			ZIO_CHECKSUM_BSWAP(record_cksum);
92 	}
93 	if (drr_type == DRR_END) {
94 		ZIO_SET_CHECKSUM(stream_cksum, 0, 0, 0, 0);
95 	} else {
96 		fletcher_4_incremental(swap, record_cksum,
97 		    sizeof (drr->drr_u.drr_checksum.drr_checksum),
98 		    stream_cksum);
99 		if (item->dp_payload_size > 0) {
100 			fletcher_4_incremental(swap, item->dp_payload,
101 			    item->dp_payload_size, stream_cksum);
102 		}
103 	}
104 	return (D_OK);
105 }
106 
107 static disposition_t
chain_validate_fletcher4(drr_packet_t * item,zio_cksum_t * stream_cksum)108 chain_validate_fletcher4(drr_packet_t *item, zio_cksum_t *stream_cksum)
109 {
110 	if (item == NULL || OPTION_ENABLED(CA_IGNORE_CKSUMS)) {
111 		return (D_OK);
112 	}
113 
114 	dmu_replay_record_t *drr   = &item->dp_drr;
115 	struct drr_end *drre	   = &item->dp_drr.drr_u.drr_end;
116 	zio_cksum_t *record_cksum  = &drr->drr_u.drr_checksum.drr_checksum;
117 	zio_cksum_t *end_cksum	   = &drre->drr_checksum;
118 
119 	boolean_t swap = ATTR_IS_SET(CA_BYTESWAPPED);
120 	uint32_t drr_type = swap ? BSWAP_32(drr->drr_type) : drr->drr_type;
121 
122 	if (drr_type == DRR_BEGIN) {
123 		ZIO_SET_CHECKSUM(stream_cksum, 0, 0, 0, 0);
124 	} else if (drr_type == DRR_END) {
125 		off_t stream_offset = item->dp_stream_offset + END_CK_OFFSET;
126 		validate_or_exit(stream_cksum, end_cksum, swap,
127 		    "in DRR_END record", stream_offset);
128 	}
129 	fletcher_4_incremental(swap, drr, CK_OFFSET, stream_cksum);
130 	if (drr_type != DRR_BEGIN && !IS_CONCLUSION(drr, drr_type)) {
131 		off_t stream_offset = item->dp_stream_offset + CK_OFFSET;
132 		validate_or_exit(stream_cksum, record_cksum,
133 		    swap, "at DRR record end", stream_offset);
134 	}
135 	if (drr_type == DRR_END) {
136 		ZIO_SET_CHECKSUM(stream_cksum, 0, 0, 0, 0);
137 	} else {
138 		fletcher_4_incremental(swap, record_cksum,
139 		    sizeof (drr->drr_u.drr_checksum.drr_checksum),
140 		    stream_cksum);
141 		if (item->dp_payload_size > 0) {
142 			fletcher_4_incremental(swap, item->dp_payload,
143 			    item->dp_payload_size, stream_cksum);
144 		}
145 	}
146 	return (D_OK);
147 }
148 
149 static chain_step_t
fletcher4_serial_step(fletcher4_op_t operation)150 fletcher4_serial_step(fletcher4_op_t operation)
151 {
152 	int context_ix = next_context++ % MAX_FLETCHER_4;
153 	fletcher4_context_t *context = &fletcher4_contexts[context_ix];
154 
155 	ZIO_SET_CHECKSUM(context, 0, 0, 0, 0);
156 	chain_step_t step = {
157 		.cs_type = CS_SERIAL,
158 		.cs_in_size = sizeof (drr_packet_t),
159 		.cs_out_size = sizeof (drr_packet_t),
160 		.cs_context = context,
161 		.cs_serial = {
162 			.process = (zc_serial_process_f *)
163 			    ((operation == F4_VALIDATE) ?
164 			    chain_validate_fletcher4 : chain_add_fletcher4)
165 		}
166 	};
167 	return (step);
168 }
169 
170 chain_step_t
serial_add_fletcher4(void)171 serial_add_fletcher4(void)
172 {
173 	return (fletcher4_serial_step(F4_SET));
174 }
175 
176 chain_step_t
serial_validate_fletcher4(void)177 serial_validate_fletcher4(void)
178 {
179 	return (fletcher4_serial_step(F4_VALIDATE));
180 }
181