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 <err.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <sys/stdtypes.h>
25 #include <sys/zfs_ioctl.h>
26 #include <sys/zio_compress.h>
27
28 #include "zstream_modules.h"
29
30 /*
31 * Validate consistency and well-formedness of the actual DRR records. I
32 * have swept all the existing validation code into this module, but it's
33 * still pretty sparse.
34 */
35
36 #define MAX_VALIDATIONS 4
37
38 typedef struct {
39 int nesting;
40 } validate_context_t;
41
42 static validate_context_t contexts[MAX_VALIDATIONS];
43 static int next_context = 0;
44
45 static disposition_t
chain_validate_records(drr_packet_t * item,validate_context_t * context)46 chain_validate_records(drr_packet_t *item, validate_context_t *context)
47 {
48 if (item == NULL)
49 return (D_OK);
50
51 struct dmu_replay_record *drr = &item->dp_drr;
52 struct drr_write *drrw = &drr->drr_u.drr_write;
53 struct drr_object *drro = &drr->drr_u.drr_object;
54
55 if (OPTION_ENABLED(CA_DO_NOT_VALIDATE))
56 return (D_OK);
57
58 if (item->dp_stream_offset == 0 && drr->drr_type != DRR_BEGIN) {
59 warnx("warning - first record is not DRR_BEGIN");
60 }
61
62 if (drr->drr_type == DRR_BEGIN) {
63 VERIFY0(context->nesting);
64 context->nesting++;
65 } else if (drr->drr_type == DRR_END) {
66 VERIFY3S(context->nesting, >=, 0);
67 if (context->nesting > 0)
68 context->nesting--;
69 } else if (drr->drr_type >= DRR_NUMTYPES) {
70 errx(1, "unknown record type: %d", drr->drr_type);
71 } else {
72 VERIFY3S(context->nesting, ==, 1);
73 }
74
75 switch (drr->drr_type) {
76
77 case DRR_BEGIN:
78 VERIFY3U(item->dp_payload_size, <=, 1UL << 28);
79 break;
80
81 case DRR_OBJECT:
82 {
83 boolean_t is_raw = !!(chain_attrs->ca_feature_flags &
84 DMU_BACKUP_FEATURE_RAW);
85 boolean_t bonus_gt_raw = drro->drr_bonuslen >
86 drro->drr_raw_bonuslen;
87 if (is_raw && bonus_gt_raw) {
88 fprintf(stderr,
89 "Warning: object %llu has bonuslen = "
90 "%u > raw_bonuslen = %u\n\n",
91 (u_longlong_t)drro->drr_object,
92 drro->drr_bonuslen,
93 drro->drr_raw_bonuslen);
94 }
95 break;
96 }
97
98 case DRR_WRITE:
99 if (drrw->drr_compressiontype >= ZIO_COMPRESS_FUNCTIONS) {
100 errx(1, "invalid compression type: %d",
101 drrw->drr_compressiontype);
102 }
103 break;
104
105 default:
106 break;
107 }
108
109 return (D_OK);
110 }
111
112 chain_step_t
serial_validate_records(void)113 serial_validate_records(void)
114 {
115 int context_ix = next_context++ % MAX_VALIDATIONS;
116 validate_context_t *context = &contexts[context_ix];
117 context->nesting = 0;
118
119 chain_step_t step = {
120 .cs_type = CS_SERIAL,
121 .cs_in_size = sizeof (drr_packet_t),
122 .cs_out_size = sizeof (drr_packet_t),
123 .cs_context = context,
124 .cs_serial = {
125 .process = (zc_serial_process_f *)chain_validate_records,
126 }
127 };
128 return (step);
129 }
130