xref: /freebsd/sys/contrib/openzfs/cmd/zstream/zstream_validate.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 <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