xref: /freebsd/sys/contrib/openzfs/cmd/zstream/zstream_validate.c (revision d0b3ecdc274930e190ea233b6b69ff03782eaf8d)
1*d0b3ecdcSMartin Matuska // SPDX-License-Identifier: CDDL-1.0
2*d0b3ecdcSMartin Matuska /*
3*d0b3ecdcSMartin Matuska  * CDDL HEADER START
4*d0b3ecdcSMartin Matuska  *
5*d0b3ecdcSMartin Matuska  * This file and its contents are supplied under the terms of the Common
6*d0b3ecdcSMartin Matuska  * Development and Distribution License ("CDDL"), version 1.0. You may only use
7*d0b3ecdcSMartin Matuska  * this file in accordance with the terms of version 1.0 of the CDDL.
8*d0b3ecdcSMartin Matuska  *
9*d0b3ecdcSMartin Matuska  * A full copy of the text of the CDDL should have accompanied this source. A
10*d0b3ecdcSMartin Matuska  * copy of the CDDL is also available via the Internet at
11*d0b3ecdcSMartin Matuska  * http://www.illumos.org/license/CDDL.
12*d0b3ecdcSMartin Matuska  *
13*d0b3ecdcSMartin Matuska  * CDDL HEADER END
14*d0b3ecdcSMartin Matuska  */
15*d0b3ecdcSMartin Matuska 
16*d0b3ecdcSMartin Matuska /*
17*d0b3ecdcSMartin Matuska  * Copyright (c) 2026 by Garth Snyder. All rights reserved.
18*d0b3ecdcSMartin Matuska  */
19*d0b3ecdcSMartin Matuska 
20*d0b3ecdcSMartin Matuska #include <assert.h>
21*d0b3ecdcSMartin Matuska #include <err.h>
22*d0b3ecdcSMartin Matuska #include <stdio.h>
23*d0b3ecdcSMartin Matuska #include <stdlib.h>
24*d0b3ecdcSMartin Matuska #include <sys/stdtypes.h>
25*d0b3ecdcSMartin Matuska #include <sys/zfs_ioctl.h>
26*d0b3ecdcSMartin Matuska #include <sys/zio_compress.h>
27*d0b3ecdcSMartin Matuska 
28*d0b3ecdcSMartin Matuska #include "zstream_modules.h"
29*d0b3ecdcSMartin Matuska 
30*d0b3ecdcSMartin Matuska /*
31*d0b3ecdcSMartin Matuska  * Validate consistency and well-formedness of the actual DRR records. I
32*d0b3ecdcSMartin Matuska  * have swept all the existing validation code into this module, but it's
33*d0b3ecdcSMartin Matuska  * still pretty sparse.
34*d0b3ecdcSMartin Matuska  */
35*d0b3ecdcSMartin Matuska 
36*d0b3ecdcSMartin Matuska #define	MAX_VALIDATIONS 4
37*d0b3ecdcSMartin Matuska 
38*d0b3ecdcSMartin Matuska typedef struct {
39*d0b3ecdcSMartin Matuska 	int	nesting;
40*d0b3ecdcSMartin Matuska } validate_context_t;
41*d0b3ecdcSMartin Matuska 
42*d0b3ecdcSMartin Matuska static validate_context_t 	contexts[MAX_VALIDATIONS];
43*d0b3ecdcSMartin Matuska static int			next_context = 0;
44*d0b3ecdcSMartin Matuska 
45*d0b3ecdcSMartin Matuska static disposition_t
chain_validate_records(drr_packet_t * item,validate_context_t * context)46*d0b3ecdcSMartin Matuska chain_validate_records(drr_packet_t *item, validate_context_t *context)
47*d0b3ecdcSMartin Matuska {
48*d0b3ecdcSMartin Matuska 	if (item == NULL)
49*d0b3ecdcSMartin Matuska 		return (D_OK);
50*d0b3ecdcSMartin Matuska 
51*d0b3ecdcSMartin Matuska 	struct dmu_replay_record *drr	= &item->dp_drr;
52*d0b3ecdcSMartin Matuska 	struct drr_write *drrw		= &drr->drr_u.drr_write;
53*d0b3ecdcSMartin Matuska 	struct drr_object *drro		= &drr->drr_u.drr_object;
54*d0b3ecdcSMartin Matuska 
55*d0b3ecdcSMartin Matuska 	if (OPTION_ENABLED(CA_DO_NOT_VALIDATE))
56*d0b3ecdcSMartin Matuska 		return (D_OK);
57*d0b3ecdcSMartin Matuska 
58*d0b3ecdcSMartin Matuska 	if (item->dp_stream_offset == 0 && drr->drr_type != DRR_BEGIN) {
59*d0b3ecdcSMartin Matuska 		warnx("warning - first record is not DRR_BEGIN");
60*d0b3ecdcSMartin Matuska 	}
61*d0b3ecdcSMartin Matuska 
62*d0b3ecdcSMartin Matuska 	if (drr->drr_type == DRR_BEGIN) {
63*d0b3ecdcSMartin Matuska 		VERIFY0(context->nesting);
64*d0b3ecdcSMartin Matuska 		context->nesting++;
65*d0b3ecdcSMartin Matuska 	} else if (drr->drr_type == DRR_END) {
66*d0b3ecdcSMartin Matuska 		VERIFY3S(context->nesting, >=, 0);
67*d0b3ecdcSMartin Matuska 		if (context->nesting > 0)
68*d0b3ecdcSMartin Matuska 			context->nesting--;
69*d0b3ecdcSMartin Matuska 	} else if (drr->drr_type >= DRR_NUMTYPES) {
70*d0b3ecdcSMartin Matuska 		errx(1, "unknown record type: %d", drr->drr_type);
71*d0b3ecdcSMartin Matuska 	} else {
72*d0b3ecdcSMartin Matuska 		VERIFY3S(context->nesting, ==, 1);
73*d0b3ecdcSMartin Matuska 	}
74*d0b3ecdcSMartin Matuska 
75*d0b3ecdcSMartin Matuska 	switch (drr->drr_type) {
76*d0b3ecdcSMartin Matuska 
77*d0b3ecdcSMartin Matuska 	case DRR_BEGIN:
78*d0b3ecdcSMartin Matuska 		VERIFY3U(item->dp_payload_size, <=, 1UL << 28);
79*d0b3ecdcSMartin Matuska 		break;
80*d0b3ecdcSMartin Matuska 
81*d0b3ecdcSMartin Matuska 	case DRR_OBJECT:
82*d0b3ecdcSMartin Matuska 		{
83*d0b3ecdcSMartin Matuska 			boolean_t is_raw = !!(chain_attrs->ca_feature_flags &
84*d0b3ecdcSMartin Matuska 			    DMU_BACKUP_FEATURE_RAW);
85*d0b3ecdcSMartin Matuska 			boolean_t bonus_gt_raw = drro->drr_bonuslen >
86*d0b3ecdcSMartin Matuska 			    drro->drr_raw_bonuslen;
87*d0b3ecdcSMartin Matuska 			if (is_raw && bonus_gt_raw) {
88*d0b3ecdcSMartin Matuska 				fprintf(stderr,
89*d0b3ecdcSMartin Matuska 				    "Warning: object %llu has bonuslen = "
90*d0b3ecdcSMartin Matuska 				    "%u > raw_bonuslen = %u\n\n",
91*d0b3ecdcSMartin Matuska 				    (u_longlong_t)drro->drr_object,
92*d0b3ecdcSMartin Matuska 				    drro->drr_bonuslen,
93*d0b3ecdcSMartin Matuska 				    drro->drr_raw_bonuslen);
94*d0b3ecdcSMartin Matuska 			}
95*d0b3ecdcSMartin Matuska 			break;
96*d0b3ecdcSMartin Matuska 		}
97*d0b3ecdcSMartin Matuska 
98*d0b3ecdcSMartin Matuska 	case DRR_WRITE:
99*d0b3ecdcSMartin Matuska 		if (drrw->drr_compressiontype >= ZIO_COMPRESS_FUNCTIONS) {
100*d0b3ecdcSMartin Matuska 			errx(1, "invalid compression type: %d",
101*d0b3ecdcSMartin Matuska 			    drrw->drr_compressiontype);
102*d0b3ecdcSMartin Matuska 		}
103*d0b3ecdcSMartin Matuska 		break;
104*d0b3ecdcSMartin Matuska 
105*d0b3ecdcSMartin Matuska 	default:
106*d0b3ecdcSMartin Matuska 		break;
107*d0b3ecdcSMartin Matuska 	}
108*d0b3ecdcSMartin Matuska 
109*d0b3ecdcSMartin Matuska 	return (D_OK);
110*d0b3ecdcSMartin Matuska }
111*d0b3ecdcSMartin Matuska 
112*d0b3ecdcSMartin Matuska chain_step_t
serial_validate_records(void)113*d0b3ecdcSMartin Matuska serial_validate_records(void)
114*d0b3ecdcSMartin Matuska {
115*d0b3ecdcSMartin Matuska 	int context_ix = next_context++ % MAX_VALIDATIONS;
116*d0b3ecdcSMartin Matuska 	validate_context_t *context = &contexts[context_ix];
117*d0b3ecdcSMartin Matuska 	context->nesting = 0;
118*d0b3ecdcSMartin Matuska 
119*d0b3ecdcSMartin Matuska 	chain_step_t step = {
120*d0b3ecdcSMartin Matuska 		.cs_type = CS_SERIAL,
121*d0b3ecdcSMartin Matuska 		.cs_in_size = sizeof (drr_packet_t),
122*d0b3ecdcSMartin Matuska 		.cs_out_size = sizeof (drr_packet_t),
123*d0b3ecdcSMartin Matuska 		.cs_context = context,
124*d0b3ecdcSMartin Matuska 		.cs_serial = {
125*d0b3ecdcSMartin Matuska 		    .process = (zc_serial_process_f *)chain_validate_records,
126*d0b3ecdcSMartin Matuska 		}
127*d0b3ecdcSMartin Matuska 	};
128*d0b3ecdcSMartin Matuska 	return (step);
129*d0b3ecdcSMartin Matuska }
130