1 // SPDX-License-Identifier: CDDL-1.0
2 /*
3 * This file and its contents are supplied under the terms of the
4 * Common Development and Distribution License ("CDDL"), version 1.0.
5 * You may only use this file in accordance with the terms of version
6 * 1.0 of the CDDL.
7 *
8 * A full copy of the text of the CDDL should have accompanied this
9 * source. A copy of the CDDL is also available via the Internet at
10 * https://opensource.org/license/CDDL-1.0.
11 */
12
13 /*
14 * Copyright 2026 ConnectWise. All rights reserved.
15 * Use is subject to license terms.
16 */
17
18 #include <err.h>
19 #include <errno.h>
20 #include <search.h>
21 #include <stdint.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/stdtypes.h>
26 #include <sys/zfs_ioctl.h>
27 #include <unistd.h>
28
29 #include "zstream.h"
30 #include "zstream_modules.h"
31
32 #define KEYSIZE 64
33
34 static disposition_t
chain_drop_records(void * item_in,void * context)35 chain_drop_records(void *item_in, void *context)
36 {
37 (void) context;
38 drr_packet_t *item = (drr_packet_t *)item_in;
39
40 if (item == NULL)
41 return (D_OK);
42
43 dmu_replay_record_t *drr = &item->dp_drr;
44 struct drr_write *drrw = &drr->drr_u.drr_write;
45 struct drr_write_embedded *drrwe = &drr->drr_u.drr_write_embedded;
46 char key[KEYSIZE];
47 u_longlong_t object, offset;
48 const char *record_type;
49 ENTRY e = {.key = key};
50
51 if (drr->drr_type == DRR_WRITE) {
52 object = drrw->drr_object;
53 offset = drrw->drr_offset;
54 record_type = "WRITE";
55 } else if (drr->drr_type == DRR_WRITE_EMBEDDED) {
56 object = drrwe->drr_object;
57 offset = drrwe->drr_offset;
58 record_type = "WRITE_EMBEDDED";
59 } else {
60 return (D_OK);
61 }
62
63 snprintf(key, KEYSIZE, "%llu,%llu", object, offset);
64 if (hsearch(e, FIND) != NULL) {
65 if (OPTION_ENABLED(CA_VERBOSE)) {
66 warnx("dropping %s record for object %llu "
67 "offset %llu", record_type, object, offset);
68 }
69 set_payload(item, NULL, 0);
70 return (D_DROP);
71 }
72
73 return (D_OK);
74 }
75
76 static chain_step_t
serial_drop_records(void)77 serial_drop_records(void)
78 {
79 chain_step_t step = {
80 .cs_type = CS_SERIAL,
81 .cs_in_size = sizeof (drr_packet_t),
82 .cs_out_size = sizeof (drr_packet_t),
83 .cs_context = NULL,
84 .cs_serial = {
85 .process = chain_drop_records
86 }
87 };
88 return (step);
89 }
90
91 int
zstream_do_drop_record(int argc,char * argv[])92 zstream_do_drop_record(int argc, char *argv[])
93 {
94 int c;
95 chain_attrs_t attrs = {0};
96
97 while ((c = getopt(argc, argv, "v")) != -1) {
98 switch (c) {
99 case 'v':
100 ENABLE_OPTION(&attrs, CA_VERBOSE);
101 break;
102 case '?':
103 warnx("invalid option '%c'\n", optopt);
104 zstream_usage();
105 }
106 }
107
108 argc -= optind;
109 argv += optind;
110
111 if (argc < 0)
112 zstream_usage();
113 if (hcreate(argc) == 0)
114 errx(1, "hcreate failed");
115
116 for (int i = 0; i < argc; i++) {
117
118 uint64_t object, offset;
119 char *obj_str;
120 char *offset_str;
121 char *key;
122 char *end;
123
124 obj_str = strsep(&argv[i], ",");
125 if (argv[i] == NULL)
126 zstream_usage();
127 errno = 0;
128 object = strtoull(obj_str, &end, 0);
129 if (errno || *end != '\0')
130 errx(1, "invalid value for object");
131 offset_str = strsep(&argv[i], ",");
132 offset = strtoull(offset_str, &end, 0);
133 if (errno || *end != '\0')
134 errx(1, "invalid value for offset");
135
136 if (asprintf(&key, "%llu,%llu", (u_longlong_t)object,
137 (u_longlong_t)offset) < 0) {
138 err(1, "asprintf");
139 }
140 ENTRY e = {.key = key};
141 ENTRY *p;
142 p = hsearch(e, ENTER);
143 if (p == NULL)
144 errx(1, "hsearch");
145 p->data = (void *)(intptr_t)B_TRUE;
146 }
147
148 ENABLE_OPTION(&attrs, CA_FORBID_DEDUP);
149
150 zstream_chain_t drop_chain = {
151 STANDARD_INPUT_STACK(NULL),
152 serial_drop_records(),
153 STANDARD_OUTPUT_STACK(NULL)
154 };
155 zstream_chain_exec(drop_chain, &attrs);
156
157 hdestroy();
158 return (0);
159 }
160