1*61145dc2SMartin Matuska // SPDX-License-Identifier: CDDL-1.0
2eda14cbcSMatt Macy /*
3eda14cbcSMatt Macy * CDDL HEADER START
4eda14cbcSMatt Macy *
5eda14cbcSMatt Macy * This file and its contents are supplied under the terms of the
6eda14cbcSMatt Macy * Common Development and Distribution License ("CDDL"), version 1.0.
7eda14cbcSMatt Macy * You may only use this file in accordance with the terms of version
8eda14cbcSMatt Macy * 1.0 of the CDDL.
9eda14cbcSMatt Macy *
10eda14cbcSMatt Macy * A full copy of the text of the CDDL should have accompanied this
11eda14cbcSMatt Macy * source. A copy of the CDDL is also available via the Internet at
12eda14cbcSMatt Macy * http://www.illumos.org/license/CDDL.
13eda14cbcSMatt Macy *
14eda14cbcSMatt Macy * CDDL HEADER END
15eda14cbcSMatt Macy */
16eda14cbcSMatt Macy
17eda14cbcSMatt Macy /*
18eda14cbcSMatt Macy * Copyright (c) 2020 by Delphix. All rights reserved.
19eda14cbcSMatt Macy */
20eda14cbcSMatt Macy
21eda14cbcSMatt Macy #include <assert.h>
22eda14cbcSMatt Macy #include <cityhash.h>
23eda14cbcSMatt Macy #include <ctype.h>
24eda14cbcSMatt Macy #include <errno.h>
25eda14cbcSMatt Macy #include <fcntl.h>
26eda14cbcSMatt Macy #include <libzfs.h>
27eda14cbcSMatt Macy #include <libzutil.h>
28eda14cbcSMatt Macy #include <stddef.h>
29eda14cbcSMatt Macy #include <stdio.h>
30eda14cbcSMatt Macy #include <stdlib.h>
31da5137abSMartin Matuska #include <string.h>
32eda14cbcSMatt Macy #include <umem.h>
33eda14cbcSMatt Macy #include <unistd.h>
34eda14cbcSMatt Macy #include <sys/debug.h>
35eda14cbcSMatt Macy #include <sys/stat.h>
36eda14cbcSMatt Macy #include <sys/zfs_ioctl.h>
37eda14cbcSMatt Macy #include <sys/zio_checksum.h>
38eda14cbcSMatt Macy #include "zfs_fletcher.h"
39eda14cbcSMatt Macy #include "zstream.h"
40eda14cbcSMatt Macy
41eda14cbcSMatt Macy
42eda14cbcSMatt Macy #define MAX_RDT_PHYSMEM_PERCENT 20
43eda14cbcSMatt Macy #define SMALLEST_POSSIBLE_MAX_RDT_MB 128
44eda14cbcSMatt Macy
45eda14cbcSMatt Macy typedef struct redup_entry {
46eda14cbcSMatt Macy struct redup_entry *rde_next;
47eda14cbcSMatt Macy uint64_t rde_guid;
48eda14cbcSMatt Macy uint64_t rde_object;
49eda14cbcSMatt Macy uint64_t rde_offset;
50eda14cbcSMatt Macy uint64_t rde_stream_offset;
51eda14cbcSMatt Macy } redup_entry_t;
52eda14cbcSMatt Macy
53eda14cbcSMatt Macy typedef struct redup_table {
54eda14cbcSMatt Macy redup_entry_t **redup_hash_array;
55eda14cbcSMatt Macy umem_cache_t *ddecache;
56eda14cbcSMatt Macy uint64_t ddt_count;
57eda14cbcSMatt Macy int numhashbits;
58eda14cbcSMatt Macy } redup_table_t;
59eda14cbcSMatt Macy
60a0b956f5SMartin Matuska void *
safe_calloc(size_t n)61eda14cbcSMatt Macy safe_calloc(size_t n)
62eda14cbcSMatt Macy {
63eda14cbcSMatt Macy void *rv = calloc(1, n);
64eda14cbcSMatt Macy if (rv == NULL) {
65eda14cbcSMatt Macy fprintf(stderr,
66eda14cbcSMatt Macy "Error: could not allocate %u bytes of memory\n",
67eda14cbcSMatt Macy (int)n);
68eda14cbcSMatt Macy exit(1);
69eda14cbcSMatt Macy }
70eda14cbcSMatt Macy return (rv);
71eda14cbcSMatt Macy }
72eda14cbcSMatt Macy
73eda14cbcSMatt Macy /*
74eda14cbcSMatt Macy * Safe version of fread(), exits on error.
75eda14cbcSMatt Macy */
76a0b956f5SMartin Matuska int
sfread(void * buf,size_t size,FILE * fp)77eda14cbcSMatt Macy sfread(void *buf, size_t size, FILE *fp)
78eda14cbcSMatt Macy {
79eda14cbcSMatt Macy int rv = fread(buf, size, 1, fp);
80eda14cbcSMatt Macy if (rv == 0 && ferror(fp)) {
81eda14cbcSMatt Macy (void) fprintf(stderr, "Error while reading file: %s\n",
82eda14cbcSMatt Macy strerror(errno));
83eda14cbcSMatt Macy exit(1);
84eda14cbcSMatt Macy }
85eda14cbcSMatt Macy return (rv);
86eda14cbcSMatt Macy }
87eda14cbcSMatt Macy
88eda14cbcSMatt Macy /*
89eda14cbcSMatt Macy * Safe version of pread(), exits on error.
90eda14cbcSMatt Macy */
91eda14cbcSMatt Macy static void
spread(int fd,void * buf,size_t count,off_t offset)92eda14cbcSMatt Macy spread(int fd, void *buf, size_t count, off_t offset)
93eda14cbcSMatt Macy {
94eda14cbcSMatt Macy ssize_t err = pread(fd, buf, count, offset);
95eda14cbcSMatt Macy if (err == -1) {
96eda14cbcSMatt Macy (void) fprintf(stderr,
97eda14cbcSMatt Macy "Error while reading file: %s\n",
98eda14cbcSMatt Macy strerror(errno));
99eda14cbcSMatt Macy exit(1);
100eda14cbcSMatt Macy } else if (err != count) {
101eda14cbcSMatt Macy (void) fprintf(stderr,
102eda14cbcSMatt Macy "Error while reading file: short read\n");
103eda14cbcSMatt Macy exit(1);
104eda14cbcSMatt Macy }
105eda14cbcSMatt Macy }
106eda14cbcSMatt Macy
107eda14cbcSMatt Macy static int
dump_record(dmu_replay_record_t * drr,void * payload,int payload_len,zio_cksum_t * zc,int outfd)108eda14cbcSMatt Macy dump_record(dmu_replay_record_t *drr, void *payload, int payload_len,
109eda14cbcSMatt Macy zio_cksum_t *zc, int outfd)
110eda14cbcSMatt Macy {
111eda14cbcSMatt Macy assert(offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum)
112eda14cbcSMatt Macy == sizeof (dmu_replay_record_t) - sizeof (zio_cksum_t));
113eda14cbcSMatt Macy fletcher_4_incremental_native(drr,
114eda14cbcSMatt Macy offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum), zc);
115eda14cbcSMatt Macy if (drr->drr_type != DRR_BEGIN) {
116eda14cbcSMatt Macy assert(ZIO_CHECKSUM_IS_ZERO(&drr->drr_u.
117eda14cbcSMatt Macy drr_checksum.drr_checksum));
118eda14cbcSMatt Macy drr->drr_u.drr_checksum.drr_checksum = *zc;
119eda14cbcSMatt Macy }
120eda14cbcSMatt Macy fletcher_4_incremental_native(&drr->drr_u.drr_checksum.drr_checksum,
121eda14cbcSMatt Macy sizeof (zio_cksum_t), zc);
122eda14cbcSMatt Macy if (write(outfd, drr, sizeof (*drr)) == -1)
123eda14cbcSMatt Macy return (errno);
124eda14cbcSMatt Macy if (payload_len != 0) {
125eda14cbcSMatt Macy fletcher_4_incremental_native(payload, payload_len, zc);
126eda14cbcSMatt Macy if (write(outfd, payload, payload_len) == -1)
127eda14cbcSMatt Macy return (errno);
128eda14cbcSMatt Macy }
129eda14cbcSMatt Macy return (0);
130eda14cbcSMatt Macy }
131eda14cbcSMatt Macy
132eda14cbcSMatt Macy static void
rdt_insert(redup_table_t * rdt,uint64_t guid,uint64_t object,uint64_t offset,uint64_t stream_offset)133eda14cbcSMatt Macy rdt_insert(redup_table_t *rdt,
134eda14cbcSMatt Macy uint64_t guid, uint64_t object, uint64_t offset, uint64_t stream_offset)
135eda14cbcSMatt Macy {
1367a7741afSMartin Matuska uint64_t ch = cityhash3(guid, object, offset);
137eda14cbcSMatt Macy uint64_t hashcode = BF64_GET(ch, 0, rdt->numhashbits);
138eda14cbcSMatt Macy redup_entry_t **rdepp;
139eda14cbcSMatt Macy
140eda14cbcSMatt Macy rdepp = &(rdt->redup_hash_array[hashcode]);
141eda14cbcSMatt Macy redup_entry_t *rde = umem_cache_alloc(rdt->ddecache, UMEM_NOFAIL);
142eda14cbcSMatt Macy rde->rde_next = *rdepp;
143eda14cbcSMatt Macy rde->rde_guid = guid;
144eda14cbcSMatt Macy rde->rde_object = object;
145eda14cbcSMatt Macy rde->rde_offset = offset;
146eda14cbcSMatt Macy rde->rde_stream_offset = stream_offset;
147eda14cbcSMatt Macy *rdepp = rde;
148eda14cbcSMatt Macy rdt->ddt_count++;
149eda14cbcSMatt Macy }
150eda14cbcSMatt Macy
151eda14cbcSMatt Macy static void
rdt_lookup(redup_table_t * rdt,uint64_t guid,uint64_t object,uint64_t offset,uint64_t * stream_offsetp)152eda14cbcSMatt Macy rdt_lookup(redup_table_t *rdt,
153eda14cbcSMatt Macy uint64_t guid, uint64_t object, uint64_t offset,
154eda14cbcSMatt Macy uint64_t *stream_offsetp)
155eda14cbcSMatt Macy {
1567a7741afSMartin Matuska uint64_t ch = cityhash3(guid, object, offset);
157eda14cbcSMatt Macy uint64_t hashcode = BF64_GET(ch, 0, rdt->numhashbits);
158eda14cbcSMatt Macy
159eda14cbcSMatt Macy for (redup_entry_t *rde = rdt->redup_hash_array[hashcode];
160eda14cbcSMatt Macy rde != NULL; rde = rde->rde_next) {
161eda14cbcSMatt Macy if (rde->rde_guid == guid &&
162eda14cbcSMatt Macy rde->rde_object == object &&
163eda14cbcSMatt Macy rde->rde_offset == offset) {
164eda14cbcSMatt Macy *stream_offsetp = rde->rde_stream_offset;
165eda14cbcSMatt Macy return;
166eda14cbcSMatt Macy }
167eda14cbcSMatt Macy }
168eda14cbcSMatt Macy assert(!"could not find expected redup table entry");
169eda14cbcSMatt Macy }
170eda14cbcSMatt Macy
171eda14cbcSMatt Macy /*
172eda14cbcSMatt Macy * Convert a dedup stream (generated by "zfs send -D") to a
173eda14cbcSMatt Macy * non-deduplicated stream. The entire infd will be converted, including
174eda14cbcSMatt Macy * any substreams in a stream package (generated by "zfs send -RD"). The
175eda14cbcSMatt Macy * infd must be seekable.
176eda14cbcSMatt Macy */
177eda14cbcSMatt Macy static void
zfs_redup_stream(int infd,int outfd,boolean_t verbose)178eda14cbcSMatt Macy zfs_redup_stream(int infd, int outfd, boolean_t verbose)
179eda14cbcSMatt Macy {
180eda14cbcSMatt Macy int bufsz = SPA_MAXBLOCKSIZE;
181aca928a5SMartin Matuska dmu_replay_record_t thedrr;
182eda14cbcSMatt Macy dmu_replay_record_t *drr = &thedrr;
183eda14cbcSMatt Macy redup_table_t rdt;
184eda14cbcSMatt Macy zio_cksum_t stream_cksum;
185eda14cbcSMatt Macy uint64_t numbuckets;
186eda14cbcSMatt Macy uint64_t num_records = 0;
187eda14cbcSMatt Macy uint64_t num_write_byref_records = 0;
188eda14cbcSMatt Macy
189aca928a5SMartin Matuska memset(&thedrr, 0, sizeof (dmu_replay_record_t));
190aca928a5SMartin Matuska
191eda14cbcSMatt Macy #ifdef _ILP32
192eda14cbcSMatt Macy uint64_t max_rde_size = SMALLEST_POSSIBLE_MAX_RDT_MB << 20;
193eda14cbcSMatt Macy #else
194eda14cbcSMatt Macy uint64_t physmem = sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE);
195eda14cbcSMatt Macy uint64_t max_rde_size =
196eda14cbcSMatt Macy MAX((physmem * MAX_RDT_PHYSMEM_PERCENT) / 100,
197eda14cbcSMatt Macy SMALLEST_POSSIBLE_MAX_RDT_MB << 20);
198eda14cbcSMatt Macy #endif
199eda14cbcSMatt Macy
200eda14cbcSMatt Macy numbuckets = max_rde_size / (sizeof (redup_entry_t));
201eda14cbcSMatt Macy
202eda14cbcSMatt Macy /*
203eda14cbcSMatt Macy * numbuckets must be a power of 2. Increase number to
204eda14cbcSMatt Macy * a power of 2 if necessary.
205eda14cbcSMatt Macy */
206eda14cbcSMatt Macy if (!ISP2(numbuckets))
207eda14cbcSMatt Macy numbuckets = 1ULL << highbit64(numbuckets);
208eda14cbcSMatt Macy
209eda14cbcSMatt Macy rdt.redup_hash_array =
210eda14cbcSMatt Macy safe_calloc(numbuckets * sizeof (redup_entry_t *));
211eda14cbcSMatt Macy rdt.ddecache = umem_cache_create("rde", sizeof (redup_entry_t), 0,
212eda14cbcSMatt Macy NULL, NULL, NULL, NULL, NULL, 0);
213eda14cbcSMatt Macy rdt.numhashbits = highbit64(numbuckets) - 1;
214eda14cbcSMatt Macy rdt.ddt_count = 0;
215eda14cbcSMatt Macy
216eda14cbcSMatt Macy char *buf = safe_calloc(bufsz);
217eda14cbcSMatt Macy FILE *ofp = fdopen(infd, "r");
218eda14cbcSMatt Macy long offset = ftell(ofp);
21915f0b8c3SMartin Matuska int begin = 0;
22015f0b8c3SMartin Matuska boolean_t seen = B_FALSE;
221eda14cbcSMatt Macy while (sfread(drr, sizeof (*drr), ofp) != 0) {
222eda14cbcSMatt Macy num_records++;
223eda14cbcSMatt Macy
224eda14cbcSMatt Macy /*
225eda14cbcSMatt Macy * We need to regenerate the checksum.
226eda14cbcSMatt Macy */
227eda14cbcSMatt Macy if (drr->drr_type != DRR_BEGIN) {
228da5137abSMartin Matuska memset(&drr->drr_u.drr_checksum.drr_checksum, 0,
229eda14cbcSMatt Macy sizeof (drr->drr_u.drr_checksum.drr_checksum));
230eda14cbcSMatt Macy }
231eda14cbcSMatt Macy
232eda14cbcSMatt Macy uint64_t payload_size = 0;
233eda14cbcSMatt Macy switch (drr->drr_type) {
234eda14cbcSMatt Macy case DRR_BEGIN:
235eda14cbcSMatt Macy {
236eda14cbcSMatt Macy struct drr_begin *drrb = &drr->drr_u.drr_begin;
237eda14cbcSMatt Macy int fflags;
238eda14cbcSMatt Macy ZIO_SET_CHECKSUM(&stream_cksum, 0, 0, 0, 0);
23915f0b8c3SMartin Matuska VERIFY0(begin++);
24015f0b8c3SMartin Matuska seen = B_TRUE;
241eda14cbcSMatt Macy
242eda14cbcSMatt Macy assert(drrb->drr_magic == DMU_BACKUP_MAGIC);
243eda14cbcSMatt Macy
244eda14cbcSMatt Macy /* clear the DEDUP feature flag for this stream */
245eda14cbcSMatt Macy fflags = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo);
246eda14cbcSMatt Macy fflags &= ~(DMU_BACKUP_FEATURE_DEDUP |
247eda14cbcSMatt Macy DMU_BACKUP_FEATURE_DEDUPPROPS);
2489db44a8eSMartin Matuska /* cppcheck-suppress syntaxError */
249eda14cbcSMatt Macy DMU_SET_FEATUREFLAGS(drrb->drr_versioninfo, fflags);
250eda14cbcSMatt Macy
25115f0b8c3SMartin Matuska uint32_t sz = drr->drr_payloadlen;
25215f0b8c3SMartin Matuska
25315f0b8c3SMartin Matuska VERIFY3U(sz, <=, 1U << 28);
25415f0b8c3SMartin Matuska
255eda14cbcSMatt Macy if (sz != 0) {
256eda14cbcSMatt Macy if (sz > bufsz) {
257eda14cbcSMatt Macy free(buf);
258eda14cbcSMatt Macy buf = safe_calloc(sz);
259eda14cbcSMatt Macy bufsz = sz;
260eda14cbcSMatt Macy }
261eda14cbcSMatt Macy (void) sfread(buf, sz, ofp);
262eda14cbcSMatt Macy }
263eda14cbcSMatt Macy payload_size = sz;
264eda14cbcSMatt Macy break;
265eda14cbcSMatt Macy }
266eda14cbcSMatt Macy
267eda14cbcSMatt Macy case DRR_END:
268eda14cbcSMatt Macy {
269eda14cbcSMatt Macy struct drr_end *drre = &drr->drr_u.drr_end;
270eda14cbcSMatt Macy /*
27115f0b8c3SMartin Matuska * We would prefer to just check --begin == 0, but
27215f0b8c3SMartin Matuska * replication streams have an end of stream END
27315f0b8c3SMartin Matuska * record, so we must avoid tripping it.
27415f0b8c3SMartin Matuska */
27515f0b8c3SMartin Matuska VERIFY3B(seen, ==, B_TRUE);
27615f0b8c3SMartin Matuska begin--;
27715f0b8c3SMartin Matuska /*
278eda14cbcSMatt Macy * Use the recalculated checksum, unless this is
279eda14cbcSMatt Macy * the END record of a stream package, which has
280eda14cbcSMatt Macy * no checksum.
281eda14cbcSMatt Macy */
282eda14cbcSMatt Macy if (!ZIO_CHECKSUM_IS_ZERO(&drre->drr_checksum))
283eda14cbcSMatt Macy drre->drr_checksum = stream_cksum;
284eda14cbcSMatt Macy break;
285eda14cbcSMatt Macy }
286eda14cbcSMatt Macy
287eda14cbcSMatt Macy case DRR_OBJECT:
288eda14cbcSMatt Macy {
289eda14cbcSMatt Macy struct drr_object *drro = &drr->drr_u.drr_object;
29015f0b8c3SMartin Matuska VERIFY3S(begin, ==, 1);
291eda14cbcSMatt Macy
292eda14cbcSMatt Macy if (drro->drr_bonuslen > 0) {
293eda14cbcSMatt Macy payload_size = DRR_OBJECT_PAYLOAD_SIZE(drro);
294eda14cbcSMatt Macy (void) sfread(buf, payload_size, ofp);
295eda14cbcSMatt Macy }
296eda14cbcSMatt Macy break;
297eda14cbcSMatt Macy }
298eda14cbcSMatt Macy
299eda14cbcSMatt Macy case DRR_SPILL:
300eda14cbcSMatt Macy {
301eda14cbcSMatt Macy struct drr_spill *drrs = &drr->drr_u.drr_spill;
30215f0b8c3SMartin Matuska VERIFY3S(begin, ==, 1);
303eda14cbcSMatt Macy payload_size = DRR_SPILL_PAYLOAD_SIZE(drrs);
304eda14cbcSMatt Macy (void) sfread(buf, payload_size, ofp);
305eda14cbcSMatt Macy break;
306eda14cbcSMatt Macy }
307eda14cbcSMatt Macy
308eda14cbcSMatt Macy case DRR_WRITE_BYREF:
309eda14cbcSMatt Macy {
310eda14cbcSMatt Macy struct drr_write_byref drrwb =
311eda14cbcSMatt Macy drr->drr_u.drr_write_byref;
31215f0b8c3SMartin Matuska VERIFY3S(begin, ==, 1);
313eda14cbcSMatt Macy
314eda14cbcSMatt Macy num_write_byref_records++;
315eda14cbcSMatt Macy
316eda14cbcSMatt Macy /*
317eda14cbcSMatt Macy * Look up in hash table by drrwb->drr_refguid,
318eda14cbcSMatt Macy * drr_refobject, drr_refoffset. Replace this
319eda14cbcSMatt Macy * record with the found WRITE record, but with
320eda14cbcSMatt Macy * drr_object,drr_offset,drr_toguid replaced with ours.
321eda14cbcSMatt Macy */
322eda14cbcSMatt Macy uint64_t stream_offset = 0;
323eda14cbcSMatt Macy rdt_lookup(&rdt, drrwb.drr_refguid,
324eda14cbcSMatt Macy drrwb.drr_refobject, drrwb.drr_refoffset,
325eda14cbcSMatt Macy &stream_offset);
326eda14cbcSMatt Macy
327eda14cbcSMatt Macy spread(infd, drr, sizeof (*drr), stream_offset);
328eda14cbcSMatt Macy
329eda14cbcSMatt Macy assert(drr->drr_type == DRR_WRITE);
330eda14cbcSMatt Macy struct drr_write *drrw = &drr->drr_u.drr_write;
331eda14cbcSMatt Macy assert(drrw->drr_toguid == drrwb.drr_refguid);
332eda14cbcSMatt Macy assert(drrw->drr_object == drrwb.drr_refobject);
333eda14cbcSMatt Macy assert(drrw->drr_offset == drrwb.drr_refoffset);
334eda14cbcSMatt Macy
335eda14cbcSMatt Macy payload_size = DRR_WRITE_PAYLOAD_SIZE(drrw);
336eda14cbcSMatt Macy spread(infd, buf, payload_size,
337eda14cbcSMatt Macy stream_offset + sizeof (*drr));
338eda14cbcSMatt Macy
339eda14cbcSMatt Macy drrw->drr_toguid = drrwb.drr_toguid;
340eda14cbcSMatt Macy drrw->drr_object = drrwb.drr_object;
341eda14cbcSMatt Macy drrw->drr_offset = drrwb.drr_offset;
342eda14cbcSMatt Macy break;
343eda14cbcSMatt Macy }
344eda14cbcSMatt Macy
345eda14cbcSMatt Macy case DRR_WRITE:
346eda14cbcSMatt Macy {
347eda14cbcSMatt Macy struct drr_write *drrw = &drr->drr_u.drr_write;
34815f0b8c3SMartin Matuska VERIFY3S(begin, ==, 1);
349eda14cbcSMatt Macy payload_size = DRR_WRITE_PAYLOAD_SIZE(drrw);
350eda14cbcSMatt Macy (void) sfread(buf, payload_size, ofp);
351eda14cbcSMatt Macy
352eda14cbcSMatt Macy rdt_insert(&rdt, drrw->drr_toguid,
353eda14cbcSMatt Macy drrw->drr_object, drrw->drr_offset, offset);
354eda14cbcSMatt Macy break;
355eda14cbcSMatt Macy }
356eda14cbcSMatt Macy
357eda14cbcSMatt Macy case DRR_WRITE_EMBEDDED:
358eda14cbcSMatt Macy {
359eda14cbcSMatt Macy struct drr_write_embedded *drrwe =
360eda14cbcSMatt Macy &drr->drr_u.drr_write_embedded;
36115f0b8c3SMartin Matuska VERIFY3S(begin, ==, 1);
362eda14cbcSMatt Macy payload_size =
363eda14cbcSMatt Macy P2ROUNDUP((uint64_t)drrwe->drr_psize, 8);
364eda14cbcSMatt Macy (void) sfread(buf, payload_size, ofp);
365eda14cbcSMatt Macy break;
366eda14cbcSMatt Macy }
367eda14cbcSMatt Macy
368eda14cbcSMatt Macy case DRR_FREEOBJECTS:
369eda14cbcSMatt Macy case DRR_FREE:
370eda14cbcSMatt Macy case DRR_OBJECT_RANGE:
37115f0b8c3SMartin Matuska VERIFY3S(begin, ==, 1);
372eda14cbcSMatt Macy break;
373eda14cbcSMatt Macy
374eda14cbcSMatt Macy default:
375eda14cbcSMatt Macy (void) fprintf(stderr, "INVALID record type 0x%x\n",
376eda14cbcSMatt Macy drr->drr_type);
377eda14cbcSMatt Macy /* should never happen, so assert */
378eda14cbcSMatt Macy assert(B_FALSE);
379eda14cbcSMatt Macy }
380eda14cbcSMatt Macy
381eda14cbcSMatt Macy if (feof(ofp)) {
382eda14cbcSMatt Macy fprintf(stderr, "Error: unexpected end-of-file\n");
383eda14cbcSMatt Macy exit(1);
384eda14cbcSMatt Macy }
385eda14cbcSMatt Macy if (ferror(ofp)) {
386eda14cbcSMatt Macy fprintf(stderr, "Error while reading file: %s\n",
387eda14cbcSMatt Macy strerror(errno));
388eda14cbcSMatt Macy exit(1);
389eda14cbcSMatt Macy }
390eda14cbcSMatt Macy
391eda14cbcSMatt Macy /*
392eda14cbcSMatt Macy * We need to recalculate the checksum, and it needs to be
393eda14cbcSMatt Macy * initially zero to do that. BEGIN records don't have
394eda14cbcSMatt Macy * a checksum.
395eda14cbcSMatt Macy */
396eda14cbcSMatt Macy if (drr->drr_type != DRR_BEGIN) {
397da5137abSMartin Matuska memset(&drr->drr_u.drr_checksum.drr_checksum, 0,
398eda14cbcSMatt Macy sizeof (drr->drr_u.drr_checksum.drr_checksum));
399eda14cbcSMatt Macy }
400eda14cbcSMatt Macy if (dump_record(drr, buf, payload_size,
401eda14cbcSMatt Macy &stream_cksum, outfd) != 0)
402eda14cbcSMatt Macy break;
403eda14cbcSMatt Macy if (drr->drr_type == DRR_END) {
404eda14cbcSMatt Macy /*
405eda14cbcSMatt Macy * Typically the END record is either the last
406eda14cbcSMatt Macy * thing in the stream, or it is followed
407eda14cbcSMatt Macy * by a BEGIN record (which also zeros the checksum).
408eda14cbcSMatt Macy * However, a stream package ends with two END
409eda14cbcSMatt Macy * records. The last END record's checksum starts
410eda14cbcSMatt Macy * from zero.
411eda14cbcSMatt Macy */
412eda14cbcSMatt Macy ZIO_SET_CHECKSUM(&stream_cksum, 0, 0, 0, 0);
413eda14cbcSMatt Macy }
414eda14cbcSMatt Macy offset = ftell(ofp);
415eda14cbcSMatt Macy }
416eda14cbcSMatt Macy
417eda14cbcSMatt Macy if (verbose) {
418eda14cbcSMatt Macy char mem_str[16];
419eda14cbcSMatt Macy zfs_nicenum(rdt.ddt_count * sizeof (redup_entry_t),
420eda14cbcSMatt Macy mem_str, sizeof (mem_str));
421eda14cbcSMatt Macy fprintf(stderr, "converted stream with %llu total records, "
422eda14cbcSMatt Macy "including %llu dedup records, using %sB memory.\n",
423eda14cbcSMatt Macy (long long)num_records,
424eda14cbcSMatt Macy (long long)num_write_byref_records,
425eda14cbcSMatt Macy mem_str);
426eda14cbcSMatt Macy }
427eda14cbcSMatt Macy
428eda14cbcSMatt Macy umem_cache_destroy(rdt.ddecache);
429eda14cbcSMatt Macy free(rdt.redup_hash_array);
430eda14cbcSMatt Macy free(buf);
431eda14cbcSMatt Macy (void) fclose(ofp);
432eda14cbcSMatt Macy }
433eda14cbcSMatt Macy
434eda14cbcSMatt Macy int
zstream_do_redup(int argc,char * argv[])435eda14cbcSMatt Macy zstream_do_redup(int argc, char *argv[])
436eda14cbcSMatt Macy {
437eda14cbcSMatt Macy boolean_t verbose = B_FALSE;
4387877fdebSMatt Macy int c;
439eda14cbcSMatt Macy
440eda14cbcSMatt Macy while ((c = getopt(argc, argv, "v")) != -1) {
441eda14cbcSMatt Macy switch (c) {
442eda14cbcSMatt Macy case 'v':
443eda14cbcSMatt Macy verbose = B_TRUE;
444eda14cbcSMatt Macy break;
445eda14cbcSMatt Macy case '?':
446eda14cbcSMatt Macy (void) fprintf(stderr, "invalid option '%c'\n",
447eda14cbcSMatt Macy optopt);
448eda14cbcSMatt Macy zstream_usage();
449eda14cbcSMatt Macy break;
450eda14cbcSMatt Macy }
451eda14cbcSMatt Macy }
452eda14cbcSMatt Macy
453eda14cbcSMatt Macy argc -= optind;
454eda14cbcSMatt Macy argv += optind;
455eda14cbcSMatt Macy
456eda14cbcSMatt Macy if (argc != 1)
457eda14cbcSMatt Macy zstream_usage();
458eda14cbcSMatt Macy
459eda14cbcSMatt Macy const char *filename = argv[0];
460eda14cbcSMatt Macy
461eda14cbcSMatt Macy if (isatty(STDOUT_FILENO)) {
462eda14cbcSMatt Macy (void) fprintf(stderr,
463eda14cbcSMatt Macy "Error: Stream can not be written to a terminal.\n"
464eda14cbcSMatt Macy "You must redirect standard output.\n");
465eda14cbcSMatt Macy return (1);
466eda14cbcSMatt Macy }
467eda14cbcSMatt Macy
468eda14cbcSMatt Macy int fd = open(filename, O_RDONLY);
469eda14cbcSMatt Macy if (fd == -1) {
470eda14cbcSMatt Macy (void) fprintf(stderr,
471eda14cbcSMatt Macy "Error while opening file '%s': %s\n",
472eda14cbcSMatt Macy filename, strerror(errno));
473eda14cbcSMatt Macy exit(1);
474eda14cbcSMatt Macy }
475eda14cbcSMatt Macy
476eda14cbcSMatt Macy fletcher_4_init();
477eda14cbcSMatt Macy zfs_redup_stream(fd, STDOUT_FILENO, verbose);
478eda14cbcSMatt Macy fletcher_4_fini();
479eda14cbcSMatt Macy
480eda14cbcSMatt Macy close(fd);
481eda14cbcSMatt Macy
482eda14cbcSMatt Macy return (0);
483eda14cbcSMatt Macy }
484