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 <err.h>
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <sys/byteorder.h>
24 #include <sys/spa_checksum.h>
25 #include <sys/stdtypes.h>
26 #include <sys/zfs_ioctl.h>
27
28 #include "zstream_modules.h"
29
30 /*
31 * Mostly from dmu_recv.c
32 */
33
34 #define DO64(X) (drr->drr_u.X = BSWAP_64(drr->drr_u.X))
35 #define DO32(X) (drr->drr_u.X = BSWAP_32(drr->drr_u.X))
36
37 typedef byteswap_stage_t byteswap_context_t;
38
39 static byteswap_context_t byteswap_contexts[MAX_BYTESWAP];
40 static int next_context = 0;
41
42 static disposition_t
chain_byteswap(drr_packet_t * item,byteswap_context_t * context)43 chain_byteswap(drr_packet_t *item, byteswap_context_t *context)
44 {
45 if (item == NULL) {
46 return (D_OK);
47 }
48
49 struct dmu_replay_record *drr = &item->dp_drr;
50 boolean_t input_swapped = *context == BS_INCOMING &&
51 ATTR_IS_SET(CA_BYTESWAPPED);
52 boolean_t swap = input_swapped || (*context == BS_OUTGOING &&
53 OPTION_ENABLED(CA_BYTESWAP_ON_OUTPUT));
54 uint32_t drr_type =
55 input_swapped ? BSWAP_32(drr->drr_type) : drr->drr_type;
56
57 if (swap) {
58 byteswap_record(drr, drr_type);
59 }
60 return (D_OK);
61 }
62
63 /*
64 * Unconditionally byteswap a DMU replay record. drr_type is passed in
65 * separately because we don't know whether we're doing input or output
66 * swapping.
67 */
68 void
byteswap_record(dmu_replay_record_t * drr,uint32_t drr_type)69 byteswap_record(dmu_replay_record_t *drr, uint32_t drr_type)
70 {
71 drr->drr_type = BSWAP_32(drr->drr_type);
72 drr->drr_payloadlen = BSWAP_32(drr->drr_payloadlen);
73
74 switch (drr_type) {
75
76 case DRR_BEGIN:
77 DO64(drr_begin.drr_magic);
78 DO64(drr_begin.drr_versioninfo);
79 DO64(drr_begin.drr_creation_time);
80 DO32(drr_begin.drr_type);
81 DO32(drr_begin.drr_flags);
82 DO64(drr_begin.drr_toguid);
83 DO64(drr_begin.drr_fromguid);
84 break;
85
86 case DRR_END:
87 DO64(drr_end.drr_toguid);
88 ZIO_CHECKSUM_BSWAP(&drr->drr_u.drr_end.drr_checksum);
89 break;
90
91 case DRR_OBJECT:
92 DO64(drr_object.drr_object);
93 DO32(drr_object.drr_type);
94 DO32(drr_object.drr_bonustype);
95 DO32(drr_object.drr_blksz);
96 DO32(drr_object.drr_bonuslen);
97 DO32(drr_object.drr_raw_bonuslen);
98 DO64(drr_object.drr_toguid);
99 DO64(drr_object.drr_maxblkid);
100 break;
101
102 case DRR_FREEOBJECTS:
103 DO64(drr_freeobjects.drr_firstobj);
104 DO64(drr_freeobjects.drr_numobjs);
105 DO64(drr_freeobjects.drr_toguid);
106 break;
107
108 case DRR_WRITE:
109 DO64(drr_write.drr_object);
110 DO32(drr_write.drr_type);
111 DO64(drr_write.drr_offset);
112 DO64(drr_write.drr_logical_size);
113 DO64(drr_write.drr_toguid);
114 ZIO_CHECKSUM_BSWAP(&drr->drr_u.drr_write.drr_key.ddk_cksum);
115 DO64(drr_write.drr_key.ddk_prop);
116 DO64(drr_write.drr_compressed_size);
117 break;
118
119 case DRR_WRITE_BYREF:
120 DO64(drr_write_byref.drr_object);
121 DO64(drr_write_byref.drr_offset);
122 DO64(drr_write_byref.drr_length);
123 DO64(drr_write_byref.drr_toguid);
124 DO64(drr_write_byref.drr_refguid);
125 DO64(drr_write_byref.drr_refobject);
126 DO64(drr_write_byref.drr_refoffset);
127 ZIO_CHECKSUM_BSWAP(
128 &drr->drr_u.drr_write_byref.drr_key.ddk_cksum);
129 DO64(drr_write_byref.drr_key.ddk_prop);
130 break;
131
132 case DRR_FREE:
133 DO64(drr_free.drr_object);
134 DO64(drr_free.drr_offset);
135 DO64(drr_free.drr_length);
136 /* Note: toguid not byte-swapped in original zstream_dump.c */
137 DO64(drr_free.drr_toguid);
138 break;
139
140 case DRR_SPILL:
141 DO64(drr_spill.drr_object);
142 DO64(drr_spill.drr_length);
143 /* Note: toguid not byte-swapped in original zstream_dump.c */
144 DO64(drr_spill.drr_toguid);
145 DO64(drr_spill.drr_compressed_size);
146 DO32(drr_spill.drr_type);
147 break;
148
149 case DRR_WRITE_EMBEDDED:
150 DO64(drr_write_embedded.drr_object);
151 DO64(drr_write_embedded.drr_offset);
152 DO64(drr_write_embedded.drr_length);
153 DO64(drr_write_embedded.drr_toguid);
154 DO32(drr_write_embedded.drr_lsize);
155 DO32(drr_write_embedded.drr_psize);
156 break;
157
158 case DRR_OBJECT_RANGE:
159 DO64(drr_object_range.drr_firstobj);
160 DO64(drr_object_range.drr_numslots);
161 DO64(drr_object_range.drr_toguid);
162 break;
163
164 case DRR_REDACT:
165 DO64(drr_redact.drr_object);
166 DO64(drr_redact.drr_offset);
167 DO64(drr_redact.drr_length);
168 DO64(drr_redact.drr_toguid);
169 break;
170
171 default:
172 errx(1, "unknown record type %llu, aborting...",
173 (u_longlong_t)drr_type);
174 }
175
176 if (drr_type != DRR_BEGIN) {
177 ZIO_CHECKSUM_BSWAP(&drr->drr_u.drr_checksum.drr_checksum);
178 }
179 }
180
181 chain_step_t
serial_byteswap(byteswap_stage_t stage)182 serial_byteswap(byteswap_stage_t stage)
183 {
184 int context_ix = next_context++ % MAX_BYTESWAP;
185 byteswap_context_t *bsc = &byteswap_contexts[context_ix];
186
187 *bsc = stage;
188 chain_step_t step = {
189 .cs_type = CS_SERIAL,
190 .cs_in_size = sizeof (drr_packet_t),
191 .cs_out_size = sizeof (drr_packet_t),
192 .cs_context = bsc,
193 .cs_serial = {
194 .process = (zc_serial_process_f *)chain_byteswap,
195 }
196 };
197 return (step);
198 }
199