xref: /dflybsd-src/sbin/hammer/cmd_mirror.c (revision 69f5a58c83dcada00192aa7abe08c6275732aa5e)
1a7fbbf91SMatthew Dillon /*
2a7fbbf91SMatthew Dillon  * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
3a7fbbf91SMatthew Dillon  *
4a7fbbf91SMatthew Dillon  * This code is derived from software contributed to The DragonFly Project
5a7fbbf91SMatthew Dillon  * by Matthew Dillon <dillon@backplane.com>
6a7fbbf91SMatthew Dillon  *
7a7fbbf91SMatthew Dillon  * Redistribution and use in source and binary forms, with or without
8a7fbbf91SMatthew Dillon  * modification, are permitted provided that the following conditions
9a7fbbf91SMatthew Dillon  * are met:
10a7fbbf91SMatthew Dillon  *
11a7fbbf91SMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
12a7fbbf91SMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
13a7fbbf91SMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
14a7fbbf91SMatthew Dillon  *    notice, this list of conditions and the following disclaimer in
15a7fbbf91SMatthew Dillon  *    the documentation and/or other materials provided with the
16a7fbbf91SMatthew Dillon  *    distribution.
17a7fbbf91SMatthew Dillon  * 3. Neither the name of The DragonFly Project nor the names of its
18a7fbbf91SMatthew Dillon  *    contributors may be used to endorse or promote products derived
19a7fbbf91SMatthew Dillon  *    from this software without specific, prior written permission.
20a7fbbf91SMatthew Dillon  *
21a7fbbf91SMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22a7fbbf91SMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23a7fbbf91SMatthew Dillon  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24a7fbbf91SMatthew Dillon  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25a7fbbf91SMatthew Dillon  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26a7fbbf91SMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27a7fbbf91SMatthew Dillon  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28a7fbbf91SMatthew Dillon  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29a7fbbf91SMatthew Dillon  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30a7fbbf91SMatthew Dillon  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31a7fbbf91SMatthew Dillon  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32a7fbbf91SMatthew Dillon  * SUCH DAMAGE.
33a7fbbf91SMatthew Dillon  */
34a7fbbf91SMatthew Dillon 
35a7fbbf91SMatthew Dillon #include "hammer.h"
36a7fbbf91SMatthew Dillon 
37269cdd19SMatthew Dillon #define LINE1	0,20
38269cdd19SMatthew Dillon #define LINE2	20,78
39269cdd19SMatthew Dillon #define LINE3	90,70
40269cdd19SMatthew Dillon 
41a7fbbf91SMatthew Dillon #define SERIALBUF_SIZE	(512 * 1024)
42a7fbbf91SMatthew Dillon 
43ced4470dSMatthew Dillon typedef struct histogram {
44ced4470dSMatthew Dillon 	hammer_tid_t	tid;
45ced4470dSMatthew Dillon 	u_int64_t	bytes;
46ced4470dSMatthew Dillon } *histogram_t;
47ced4470dSMatthew Dillon 
48a7fbbf91SMatthew Dillon static int read_mrecords(int fd, char *buf, u_int size,
4917dd83bcSMatthew Dillon 			 hammer_ioc_mrecord_head_t pickup);
50e7f926a5SMatthew Dillon static int generate_histogram(int fd, const char *filesystem,
51ced4470dSMatthew Dillon 			 histogram_t *histogram_ary,
5239e88285SMatthew Dillon 			 struct hammer_ioc_mirror_rw *mirror_base,
5339e88285SMatthew Dillon 			 int *repeatp);
5417dd83bcSMatthew Dillon static hammer_ioc_mrecord_any_t read_mrecord(int fdin, int *errorp,
5517dd83bcSMatthew Dillon 			 hammer_ioc_mrecord_head_t pickup);
5617dd83bcSMatthew Dillon static void write_mrecord(int fdout, u_int32_t type,
5717dd83bcSMatthew Dillon 			 hammer_ioc_mrecord_any_t mrec, int bytes);
58e7f926a5SMatthew Dillon static void generate_mrec_header(int fd, int pfs_id,
59e7f926a5SMatthew Dillon 			 union hammer_ioc_mrecord_any *mrec_tmp);
6048eadef9SMatthew Dillon static int validate_mrec_header(int fd, int fdin, int is_target, int pfs_id,
6148eadef9SMatthew Dillon 			 struct hammer_ioc_mrecord_head *pickup,
6234ebae70SMatthew Dillon 			 hammer_tid_t *tid_begp, hammer_tid_t *tid_endp);
63d4e5b69bSMatthew Dillon static void update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id);
6448eadef9SMatthew Dillon static ssize_t writebw(int fd, const void *buf, size_t nbytes,
6548eadef9SMatthew Dillon 			u_int64_t *bwcount, struct timeval *tv1);
6601a72c9fSMatthew Dillon static int getyn(void);
67a7fbbf91SMatthew Dillon static void mirror_usage(int code);
68a7fbbf91SMatthew Dillon 
6917dd83bcSMatthew Dillon /*
7017dd83bcSMatthew Dillon  * Generate a mirroring data stream from the specific source over the
7117dd83bcSMatthew Dillon  * entire key range, but restricted to the specified transaction range.
7217dd83bcSMatthew Dillon  *
7317dd83bcSMatthew Dillon  * The HAMMER VFS does most of the work, we add a few new mrecord
7417dd83bcSMatthew Dillon  * types to negotiate the TID ranges and verify that the entire
7517dd83bcSMatthew Dillon  * stream made it to the destination.
7639e88285SMatthew Dillon  *
7739e88285SMatthew Dillon  * streaming will be 0 for mirror-read, 1 for mirror-stream.  The code will
7839e88285SMatthew Dillon  * set up a fake value of -1 when running the histogram for mirror-read.
7917dd83bcSMatthew Dillon  */
80a7fbbf91SMatthew Dillon void
8148eadef9SMatthew Dillon hammer_cmd_mirror_read(char **av, int ac, int streaming)
82a7fbbf91SMatthew Dillon {
83a7fbbf91SMatthew Dillon 	struct hammer_ioc_mirror_rw mirror;
84d4e5b69bSMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
8517dd83bcSMatthew Dillon 	union hammer_ioc_mrecord_any mrec_tmp;
8648eadef9SMatthew Dillon 	struct hammer_ioc_mrecord_head pickup;
8717dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
88243ca327SMatthew Dillon 	hammer_tid_t sync_tid;
89ced4470dSMatthew Dillon 	histogram_t histogram_ary;
90a7fbbf91SMatthew Dillon 	const char *filesystem;
91a7fbbf91SMatthew Dillon 	char *buf = malloc(SERIALBUF_SIZE);
92243ca327SMatthew Dillon 	int interrupted = 0;
93243ca327SMatthew Dillon 	int error;
94a7fbbf91SMatthew Dillon 	int fd;
95243ca327SMatthew Dillon 	int n;
9648eadef9SMatthew Dillon 	int didwork;
97e7f926a5SMatthew Dillon 	int histogram;
98ced4470dSMatthew Dillon 	int histindex;
99ced4470dSMatthew Dillon 	int histmax;
10039e88285SMatthew Dillon 	int repeat = 0;
10139e88285SMatthew Dillon 	int sameline;
10248eadef9SMatthew Dillon 	int64_t total_bytes;
103243ca327SMatthew Dillon 	time_t base_t = time(NULL);
10448eadef9SMatthew Dillon 	struct timeval bwtv;
10548eadef9SMatthew Dillon 	u_int64_t bwcount;
106ced4470dSMatthew Dillon 	u_int64_t estbytes;
107a7fbbf91SMatthew Dillon 
10834bb69d8SThomas Nikolajsen 	if (ac == 0 || ac > 2)
109a7fbbf91SMatthew Dillon 		mirror_usage(1);
110a7fbbf91SMatthew Dillon 	filesystem = av[0];
111*69f5a58cSMatthew Dillon 	hammer_check_restrict(filesystem);
112a7fbbf91SMatthew Dillon 
11348eadef9SMatthew Dillon 	pickup.signature = 0;
11448eadef9SMatthew Dillon 	pickup.type = 0;
115ced4470dSMatthew Dillon 	histogram = 0;
116ced4470dSMatthew Dillon 	histindex = 0;
117ced4470dSMatthew Dillon 	histmax = 0;
118e7f926a5SMatthew Dillon 	histogram_ary = NULL;
11939e88285SMatthew Dillon 	sameline = 0;
12048eadef9SMatthew Dillon 
12148eadef9SMatthew Dillon again:
122a7fbbf91SMatthew Dillon 	bzero(&mirror, sizeof(mirror));
123a7fbbf91SMatthew Dillon 	hammer_key_beg_init(&mirror.key_beg);
124a7fbbf91SMatthew Dillon 	hammer_key_end_init(&mirror.key_end);
125a7fbbf91SMatthew Dillon 
126d4e5b69bSMatthew Dillon 	fd = getpfs(&pfs, filesystem);
127a7fbbf91SMatthew Dillon 
128269cdd19SMatthew Dillon 	if (streaming >= 0)
129269cdd19SMatthew Dillon 		score_printf(LINE1, "Running");
130269cdd19SMatthew Dillon 
13139e88285SMatthew Dillon 	if (streaming >= 0 && VerboseOpt && VerboseOpt < 2) {
13239e88285SMatthew Dillon 		fprintf(stderr, "%cRunning  \b\b", (sameline ? '\r' : '\n'));
13348eadef9SMatthew Dillon 		fflush(stderr);
13439e88285SMatthew Dillon 		sameline = 1;
13548eadef9SMatthew Dillon 	}
13639e88285SMatthew Dillon 	sameline = 1;
13748eadef9SMatthew Dillon 	total_bytes = 0;
13848eadef9SMatthew Dillon 	gettimeofday(&bwtv, NULL);
13948eadef9SMatthew Dillon 	bwcount = 0;
14048eadef9SMatthew Dillon 
141243ca327SMatthew Dillon 	/*
142e7f926a5SMatthew Dillon 	 * Send initial header for the purpose of determining the
143e7f926a5SMatthew Dillon 	 * shared-uuid.
14401a72c9fSMatthew Dillon 	 */
145e7f926a5SMatthew Dillon 	generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp);
146e7f926a5SMatthew Dillon 	write_mrecord(1, HAMMER_MREC_TYPE_PFSD,
147e7f926a5SMatthew Dillon 		      &mrec_tmp, sizeof(mrec_tmp.pfs));
14801a72c9fSMatthew Dillon 
14901a72c9fSMatthew Dillon 	/*
150d4e5b69bSMatthew Dillon 	 * In 2-way mode the target will send us a PFS info packet
151d4e5b69bSMatthew Dillon 	 * first.  Use the target's current snapshot TID as our default
152d4e5b69bSMatthew Dillon 	 * begin TID.
153243ca327SMatthew Dillon 	 */
15448eadef9SMatthew Dillon 	if (TwoWayPipeOpt) {
15539e88285SMatthew Dillon 		mirror.tid_beg = 0;
15648eadef9SMatthew Dillon 		n = validate_mrec_header(fd, 0, 0, pfs.pfs_id, &pickup,
157d4e5b69bSMatthew Dillon 					 NULL, &mirror.tid_beg);
15848eadef9SMatthew Dillon 		if (n < 0) {	/* got TERM record */
15948eadef9SMatthew Dillon 			relpfs(fd, &pfs);
16048eadef9SMatthew Dillon 			return;
16148eadef9SMatthew Dillon 		}
16248eadef9SMatthew Dillon 		++mirror.tid_beg;
16339e88285SMatthew Dillon 	} else if (streaming && histogram) {
16439e88285SMatthew Dillon 		mirror.tid_beg = histogram_ary[histindex].tid + 1;
16539e88285SMatthew Dillon 	} else {
16639e88285SMatthew Dillon 		mirror.tid_beg = 0;
16748eadef9SMatthew Dillon 	}
168d4e5b69bSMatthew Dillon 
169d4e5b69bSMatthew Dillon 	/*
170d4e5b69bSMatthew Dillon 	 * Write out the PFS header, tid_beg will be updated if our PFS
171d4e5b69bSMatthew Dillon 	 * has a larger begin sync.  tid_end is set to the latest source
172d4e5b69bSMatthew Dillon 	 * TID whos flush cycle has completed.
173d4e5b69bSMatthew Dillon 	 */
174e7f926a5SMatthew Dillon 	generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp);
175e7f926a5SMatthew Dillon 	if (mirror.tid_beg < mrec_tmp.pfs.pfsd.sync_beg_tid)
176e7f926a5SMatthew Dillon 		mirror.tid_beg = mrec_tmp.pfs.pfsd.sync_beg_tid;
177e7f926a5SMatthew Dillon 	mirror.tid_end = mrec_tmp.pfs.pfsd.sync_end_tid;
178e7f926a5SMatthew Dillon 	mirror.ubuf = buf;
179e7f926a5SMatthew Dillon 	mirror.size = SERIALBUF_SIZE;
180e7f926a5SMatthew Dillon 	mirror.pfs_id = pfs.pfs_id;
181e7f926a5SMatthew Dillon 	mirror.shared_uuid = pfs.ondisk->shared_uuid;
182d4e5b69bSMatthew Dillon 
183d4e5b69bSMatthew Dillon 	/*
184e7f926a5SMatthew Dillon 	 * XXX If the histogram is exhausted and the TID delta is large
185e7f926a5SMatthew Dillon 	 *     the stream might have been offline for a while and is
186e7f926a5SMatthew Dillon 	 *     now picking it up again.  Do another histogram.
187d4e5b69bSMatthew Dillon 	 */
188e7f926a5SMatthew Dillon #if 0
18939e88285SMatthew Dillon 	if (streaming && histogram && histindex == histend) {
190e7f926a5SMatthew Dillon 		if (mirror.tid_end - mirror.tid_beg > BULK_MINIMUM)
191ced4470dSMatthew Dillon 			histogram = 0;
192e7f926a5SMatthew Dillon 	}
193e7f926a5SMatthew Dillon #endif
19434ebae70SMatthew Dillon 
195e7f926a5SMatthew Dillon 	/*
196e7f926a5SMatthew Dillon 	 * Initial bulk startup control, try to do some incremental
197e7f926a5SMatthew Dillon 	 * mirroring in order to allow the stream to be killed and
198e7f926a5SMatthew Dillon 	 * restarted without having to start over.
199e7f926a5SMatthew Dillon 	 */
200ced4470dSMatthew Dillon 	if (histogram == 0 && BulkOpt == 0) {
20139e88285SMatthew Dillon 		if (VerboseOpt && repeat == 0) {
202e7f926a5SMatthew Dillon 			fprintf(stderr, "\n");
20339e88285SMatthew Dillon 			sameline = 0;
20439e88285SMatthew Dillon 		}
205ced4470dSMatthew Dillon 		histmax = generate_histogram(fd, filesystem,
20639e88285SMatthew Dillon 					     &histogram_ary, &mirror,
20739e88285SMatthew Dillon 					     &repeat);
208ced4470dSMatthew Dillon 		histindex = 0;
209ced4470dSMatthew Dillon 		histogram = 1;
21039e88285SMatthew Dillon 
21139e88285SMatthew Dillon 		/*
21239e88285SMatthew Dillon 		 * Just stream the histogram, then stop
21339e88285SMatthew Dillon 		 */
21439e88285SMatthew Dillon 		if (streaming == 0)
21539e88285SMatthew Dillon 			streaming = -1;
216e7f926a5SMatthew Dillon 	}
217e7f926a5SMatthew Dillon 
21839e88285SMatthew Dillon 	if (streaming && histogram) {
219ced4470dSMatthew Dillon 		++histindex;
220ced4470dSMatthew Dillon 		mirror.tid_end = histogram_ary[histindex].tid;
221ced4470dSMatthew Dillon 		estbytes = histogram_ary[histindex-1].bytes;
222e7f926a5SMatthew Dillon 		mrec_tmp.pfs.pfsd.sync_end_tid = mirror.tid_end;
223ced4470dSMatthew Dillon 	} else {
224ced4470dSMatthew Dillon 		estbytes = 0;
225e7f926a5SMatthew Dillon 	}
226e7f926a5SMatthew Dillon 
227e7f926a5SMatthew Dillon 	write_mrecord(1, HAMMER_MREC_TYPE_PFSD,
228e7f926a5SMatthew Dillon 		      &mrec_tmp, sizeof(mrec_tmp.pfs));
229e7f926a5SMatthew Dillon 
230e7f926a5SMatthew Dillon 	/*
231e7f926a5SMatthew Dillon 	 * A cycle file overrides the beginning TID only if we are
23239e88285SMatthew Dillon 	 * not operating in two-way or histogram mode.
233e7f926a5SMatthew Dillon 	 */
23439e88285SMatthew Dillon 	if (TwoWayPipeOpt == 0 && histogram == 0) {
235e7f926a5SMatthew Dillon 		hammer_get_cycle(&mirror.key_beg, &mirror.tid_beg);
236e7f926a5SMatthew Dillon 	}
237e7f926a5SMatthew Dillon 
238e7f926a5SMatthew Dillon 	/*
239e7f926a5SMatthew Dillon 	 * An additional argument overrides the beginning TID regardless
240e7f926a5SMatthew Dillon 	 * of what mode we are in.  This is not recommending if operating
241e7f926a5SMatthew Dillon 	 * in two-way mode.
242e7f926a5SMatthew Dillon 	 */
24317dd83bcSMatthew Dillon 	if (ac == 2)
24417dd83bcSMatthew Dillon 		mirror.tid_beg = strtoull(av[1], NULL, 0);
24517dd83bcSMatthew Dillon 
24648eadef9SMatthew Dillon 	if (streaming == 0 || VerboseOpt >= 2) {
24748eadef9SMatthew Dillon 		fprintf(stderr,
248ced4470dSMatthew Dillon 			"Mirror-read: Mirror %016jx to %016jx",
249a276dc6bSMatthew Dillon 			(uintmax_t)mirror.tid_beg, (uintmax_t)mirror.tid_end);
250ced4470dSMatthew Dillon 		if (histogram)
251ced4470dSMatthew Dillon 			fprintf(stderr, " (bulk= %ju)", (uintmax_t)estbytes);
2523d7b2393SMatthew Dillon 		fprintf(stderr, "\n");
2533d7b2393SMatthew Dillon 		fflush(stderr);
25448eadef9SMatthew Dillon 	}
255243ca327SMatthew Dillon 	if (mirror.key_beg.obj_id != (int64_t)HAMMER_MIN_OBJID) {
256a276dc6bSMatthew Dillon 		fprintf(stderr, "Mirror-read: Resuming at object %016jx\n",
257a276dc6bSMatthew Dillon 			(uintmax_t)mirror.key_beg.obj_id);
258243ca327SMatthew Dillon 	}
259243ca327SMatthew Dillon 
260243ca327SMatthew Dillon 	/*
2619dc76cb1SMatthew Dillon 	 * Nothing to do if begin equals end.
2629dc76cb1SMatthew Dillon 	 */
26348eadef9SMatthew Dillon 	if (mirror.tid_beg >= mirror.tid_end) {
26448eadef9SMatthew Dillon 		if (streaming == 0 || VerboseOpt >= 2)
26548eadef9SMatthew Dillon 			fprintf(stderr, "Mirror-read: No work to do\n");
26639e88285SMatthew Dillon 		sleep(DelayOpt);
26748eadef9SMatthew Dillon 		didwork = 0;
26839e88285SMatthew Dillon 		histogram = 0;
2699dc76cb1SMatthew Dillon 		goto done;
2709dc76cb1SMatthew Dillon 	}
27148eadef9SMatthew Dillon 	didwork = 1;
2729dc76cb1SMatthew Dillon 
2739dc76cb1SMatthew Dillon 	/*
274243ca327SMatthew Dillon 	 * Write out bulk records
275243ca327SMatthew Dillon 	 */
276a7fbbf91SMatthew Dillon 	mirror.ubuf = buf;
277a7fbbf91SMatthew Dillon 	mirror.size = SERIALBUF_SIZE;
278a7fbbf91SMatthew Dillon 
279a7fbbf91SMatthew Dillon 	do {
280a7fbbf91SMatthew Dillon 		mirror.count = 0;
281d4e5b69bSMatthew Dillon 		mirror.pfs_id = pfs.pfs_id;
282d4e5b69bSMatthew Dillon 		mirror.shared_uuid = pfs.ondisk->shared_uuid;
283a7fbbf91SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) {
284269cdd19SMatthew Dillon 			score_printf(LINE3, "Mirror-read %s failed: %s",
285269cdd19SMatthew Dillon 				     filesystem, strerror(errno));
286a7fbbf91SMatthew Dillon 			fprintf(stderr, "Mirror-read %s failed: %s\n",
287a7fbbf91SMatthew Dillon 				filesystem, strerror(errno));
288a7fbbf91SMatthew Dillon 			exit(1);
289a7fbbf91SMatthew Dillon 		}
2909c67b4d2SMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) {
291269cdd19SMatthew Dillon 			score_printf(LINE3, "Mirror-read %s fatal error %d",
292269cdd19SMatthew Dillon 				     filesystem, mirror.head.error);
2939c67b4d2SMatthew Dillon 			fprintf(stderr,
2949c67b4d2SMatthew Dillon 				"Mirror-read %s fatal error %d\n",
2959c67b4d2SMatthew Dillon 				filesystem, mirror.head.error);
2969c67b4d2SMatthew Dillon 			exit(1);
2979c67b4d2SMatthew Dillon 		}
298243ca327SMatthew Dillon 		if (mirror.count) {
29948eadef9SMatthew Dillon 			if (BandwidthOpt) {
30048eadef9SMatthew Dillon 				n = writebw(1, mirror.ubuf, mirror.count,
30148eadef9SMatthew Dillon 					    &bwcount, &bwtv);
30248eadef9SMatthew Dillon 			} else {
303243ca327SMatthew Dillon 				n = write(1, mirror.ubuf, mirror.count);
30448eadef9SMatthew Dillon 			}
305243ca327SMatthew Dillon 			if (n != mirror.count) {
306269cdd19SMatthew Dillon 				score_printf(LINE3,
307269cdd19SMatthew Dillon 					     "Mirror-read %s failed: "
308269cdd19SMatthew Dillon 					     "short write",
309269cdd19SMatthew Dillon 					     filesystem);
310269cdd19SMatthew Dillon 				fprintf(stderr,
311269cdd19SMatthew Dillon 					"Mirror-read %s failed: "
312243ca327SMatthew Dillon 					"short write\n",
313243ca327SMatthew Dillon 				filesystem);
314243ca327SMatthew Dillon 				exit(1);
315243ca327SMatthew Dillon 			}
316a7fbbf91SMatthew Dillon 		}
31748eadef9SMatthew Dillon 		total_bytes += mirror.count;
31848eadef9SMatthew Dillon 		if (streaming && VerboseOpt) {
319e7f926a5SMatthew Dillon 			fprintf(stderr,
32039e88285SMatthew Dillon 				"\rscan obj=%016jx tids=%016jx:%016jx %11jd",
321a276dc6bSMatthew Dillon 				(uintmax_t)mirror.key_cur.obj_id,
322a276dc6bSMatthew Dillon 				(uintmax_t)mirror.tid_beg,
323a276dc6bSMatthew Dillon 				(uintmax_t)mirror.tid_end,
324a276dc6bSMatthew Dillon 				(intmax_t)total_bytes);
32548eadef9SMatthew Dillon 			fflush(stderr);
32639e88285SMatthew Dillon 			sameline = 0;
327269cdd19SMatthew Dillon 		} else if (streaming) {
328269cdd19SMatthew Dillon 			score_printf(LINE2,
329269cdd19SMatthew Dillon 				"obj=%016jx tids=%016jx:%016jx %11jd",
330269cdd19SMatthew Dillon 				(uintmax_t)mirror.key_cur.obj_id,
331269cdd19SMatthew Dillon 				(uintmax_t)mirror.tid_beg,
332269cdd19SMatthew Dillon 				(uintmax_t)mirror.tid_end,
333269cdd19SMatthew Dillon 				(intmax_t)total_bytes);
33448eadef9SMatthew Dillon 		}
335a7fbbf91SMatthew Dillon 		mirror.key_beg = mirror.key_cur;
336e7f926a5SMatthew Dillon 
337e7f926a5SMatthew Dillon 		/*
338e7f926a5SMatthew Dillon 		 * Deal with time limit option
339e7f926a5SMatthew Dillon 		 */
340243ca327SMatthew Dillon 		if (TimeoutOpt &&
341243ca327SMatthew Dillon 		    (unsigned)(time(NULL) - base_t) > (unsigned)TimeoutOpt) {
342269cdd19SMatthew Dillon 			score_printf(LINE3,
343269cdd19SMatthew Dillon 				"Mirror-read %s interrupted by timer at"
344269cdd19SMatthew Dillon 				" %016jx",
345269cdd19SMatthew Dillon 				filesystem,
346269cdd19SMatthew Dillon 				(uintmax_t)mirror.key_cur.obj_id);
347243ca327SMatthew Dillon 			fprintf(stderr,
348243ca327SMatthew Dillon 				"Mirror-read %s interrupted by timer at"
349a276dc6bSMatthew Dillon 				" %016jx\n",
350243ca327SMatthew Dillon 				filesystem,
351a276dc6bSMatthew Dillon 				(uintmax_t)mirror.key_cur.obj_id);
352243ca327SMatthew Dillon 			interrupted = 1;
353243ca327SMatthew Dillon 			break;
354243ca327SMatthew Dillon 		}
355a7fbbf91SMatthew Dillon 	} while (mirror.count != 0);
356a7fbbf91SMatthew Dillon 
35748eadef9SMatthew Dillon done:
35839e88285SMatthew Dillon 	if (streaming && VerboseOpt && sameline == 0) {
359ced4470dSMatthew Dillon 		fprintf(stderr, "\n");
360ced4470dSMatthew Dillon 		fflush(stderr);
36139e88285SMatthew Dillon 		sameline = 1;
362ced4470dSMatthew Dillon 	}
363ced4470dSMatthew Dillon 
364243ca327SMatthew Dillon 	/*
36548eadef9SMatthew Dillon 	 * Write out the termination sync record - only if not interrupted
366243ca327SMatthew Dillon 	 */
36748eadef9SMatthew Dillon 	if (interrupted == 0) {
36848eadef9SMatthew Dillon 		if (didwork) {
36917dd83bcSMatthew Dillon 			write_mrecord(1, HAMMER_MREC_TYPE_SYNC,
37017dd83bcSMatthew Dillon 				      &mrec_tmp, sizeof(mrec_tmp.sync));
37148eadef9SMatthew Dillon 		} else {
37248eadef9SMatthew Dillon 			write_mrecord(1, HAMMER_MREC_TYPE_IDLE,
37348eadef9SMatthew Dillon 				      &mrec_tmp, sizeof(mrec_tmp.sync));
37448eadef9SMatthew Dillon 		}
37548eadef9SMatthew Dillon 	}
37634ebae70SMatthew Dillon 
377243ca327SMatthew Dillon 	/*
378243ca327SMatthew Dillon 	 * If the -2 option was given (automatic when doing mirror-copy),
379243ca327SMatthew Dillon 	 * a two-way pipe is assumed and we expect a response mrec from
380243ca327SMatthew Dillon 	 * the target.
381243ca327SMatthew Dillon 	 */
382243ca327SMatthew Dillon 	if (TwoWayPipeOpt) {
38348eadef9SMatthew Dillon 		mrec = read_mrecord(0, &error, &pickup);
38417dd83bcSMatthew Dillon 		if (mrec == NULL ||
38517dd83bcSMatthew Dillon 		    mrec->head.type != HAMMER_MREC_TYPE_UPDATE ||
38617dd83bcSMatthew Dillon 		    mrec->head.rec_size != sizeof(mrec->update)) {
387243ca327SMatthew Dillon 			fprintf(stderr, "mirror_read: Did not get final "
388243ca327SMatthew Dillon 					"acknowledgement packet from target\n");
389243ca327SMatthew Dillon 			exit(1);
390243ca327SMatthew Dillon 		}
391243ca327SMatthew Dillon 		if (interrupted) {
392243ca327SMatthew Dillon 			if (CyclePath) {
393ced4470dSMatthew Dillon 				hammer_set_cycle(&mirror.key_cur,
394ced4470dSMatthew Dillon 						 mirror.tid_beg);
39534bb69d8SThomas Nikolajsen 				fprintf(stderr, "Cyclefile %s updated for "
39634bb69d8SThomas Nikolajsen 					"continuation\n", CyclePath);
397243ca327SMatthew Dillon 			}
398243ca327SMatthew Dillon 		} else {
39917dd83bcSMatthew Dillon 			sync_tid = mrec->update.tid;
400243ca327SMatthew Dillon 			if (CyclePath) {
401243ca327SMatthew Dillon 				hammer_key_beg_init(&mirror.key_beg);
402243ca327SMatthew Dillon 				hammer_set_cycle(&mirror.key_beg, sync_tid);
403a276dc6bSMatthew Dillon 				fprintf(stderr,
404a276dc6bSMatthew Dillon 					"Cyclefile %s updated to 0x%016jx\n",
405a276dc6bSMatthew Dillon 					CyclePath, (uintmax_t)sync_tid);
406243ca327SMatthew Dillon 			}
407243ca327SMatthew Dillon 		}
408243ca327SMatthew Dillon 	} else if (CyclePath) {
409243ca327SMatthew Dillon 		/* NOTE! mirror.tid_beg cannot be updated */
410243ca327SMatthew Dillon 		fprintf(stderr, "Warning: cycle file (-c option) cannot be "
411243ca327SMatthew Dillon 				"fully updated unless you use mirror-copy\n");
412243ca327SMatthew Dillon 		hammer_set_cycle(&mirror.key_beg, mirror.tid_beg);
413243ca327SMatthew Dillon 	}
41448eadef9SMatthew Dillon 	if (streaming && interrupted == 0) {
41548eadef9SMatthew Dillon 		time_t t1 = time(NULL);
41648eadef9SMatthew Dillon 		time_t t2;
41748eadef9SMatthew Dillon 
418e7f926a5SMatthew Dillon 		/*
41939e88285SMatthew Dillon 		 * Try to break down large bulk transfers into smaller ones
42039e88285SMatthew Dillon 		 * so it can sync the transaction id on the slave.  This
42139e88285SMatthew Dillon 		 * way if we get interrupted a restart doesn't have to
42239e88285SMatthew Dillon 		 * start from scratch.
423e7f926a5SMatthew Dillon 		 */
42439e88285SMatthew Dillon 		if (streaming && histogram) {
425ced4470dSMatthew Dillon 			if (histindex != histmax) {
42639e88285SMatthew Dillon 				if (VerboseOpt && VerboseOpt < 2 &&
42739e88285SMatthew Dillon 				    streaming >= 0) {
428e7f926a5SMatthew Dillon 					fprintf(stderr, " (bulk incremental)");
42939e88285SMatthew Dillon 				}
43039e88285SMatthew Dillon 				relpfs(fd, &pfs);
431e7f926a5SMatthew Dillon 				goto again;
432e7f926a5SMatthew Dillon 			}
433ced4470dSMatthew Dillon 		}
434e7f926a5SMatthew Dillon 
43539e88285SMatthew Dillon 		if (VerboseOpt && streaming >= 0) {
43648eadef9SMatthew Dillon 			fprintf(stderr, " W");
43748eadef9SMatthew Dillon 			fflush(stderr);
438269cdd19SMatthew Dillon 		} else if (streaming >= 0) {
439269cdd19SMatthew Dillon 			score_printf(LINE1, "Waiting");
44048eadef9SMatthew Dillon 		}
44148eadef9SMatthew Dillon 		pfs.ondisk->sync_end_tid = mirror.tid_end;
44239e88285SMatthew Dillon 		if (streaming < 0) {
44339e88285SMatthew Dillon 			/*
44439e88285SMatthew Dillon 			 * Fake streaming mode when using a histogram to
44539e88285SMatthew Dillon 			 * break up a mirror-read, do not wait on source.
44639e88285SMatthew Dillon 			 */
44739e88285SMatthew Dillon 			streaming = 0;
44839e88285SMatthew Dillon 		} else if (ioctl(fd, HAMMERIOC_WAI_PSEUDOFS, &pfs) < 0) {
449269cdd19SMatthew Dillon 			score_printf(LINE3,
450269cdd19SMatthew Dillon 				     "Mirror-read %s: cannot stream: %s\n",
451269cdd19SMatthew Dillon 				     filesystem, strerror(errno));
452269cdd19SMatthew Dillon 			fprintf(stderr,
453269cdd19SMatthew Dillon 				"Mirror-read %s: cannot stream: %s\n",
45448eadef9SMatthew Dillon 				filesystem, strerror(errno));
45548eadef9SMatthew Dillon 		} else {
45648eadef9SMatthew Dillon 			t2 = time(NULL) - t1;
45748eadef9SMatthew Dillon 			if (t2 >= 0 && t2 < DelayOpt) {
45848eadef9SMatthew Dillon 				if (VerboseOpt) {
45948eadef9SMatthew Dillon 					fprintf(stderr, "\bD");
46048eadef9SMatthew Dillon 					fflush(stderr);
46148eadef9SMatthew Dillon 				}
46248eadef9SMatthew Dillon 				sleep(DelayOpt - t2);
46348eadef9SMatthew Dillon 			}
46448eadef9SMatthew Dillon 			if (VerboseOpt) {
46548eadef9SMatthew Dillon 				fprintf(stderr, "\b ");
46648eadef9SMatthew Dillon 				fflush(stderr);
46748eadef9SMatthew Dillon 			}
46848eadef9SMatthew Dillon 			relpfs(fd, &pfs);
46948eadef9SMatthew Dillon 			goto again;
47048eadef9SMatthew Dillon 		}
47148eadef9SMatthew Dillon 	}
47248eadef9SMatthew Dillon 	write_mrecord(1, HAMMER_MREC_TYPE_TERM,
47348eadef9SMatthew Dillon 		      &mrec_tmp, sizeof(mrec_tmp.sync));
47448eadef9SMatthew Dillon 	relpfs(fd, &pfs);
475a7fbbf91SMatthew Dillon 	fprintf(stderr, "Mirror-read %s succeeded\n", filesystem);
476a7fbbf91SMatthew Dillon }
477a7fbbf91SMatthew Dillon 
478e7f926a5SMatthew Dillon /*
4793d7b2393SMatthew Dillon  * What we are trying to do here is figure out how much data is
4803d7b2393SMatthew Dillon  * going to be sent for the TID range and to break the TID range
4813d7b2393SMatthew Dillon  * down into reasonably-sized slices (from the point of view of
4823d7b2393SMatthew Dillon  * data sent) so a lost connection can restart at a reasonable
4833d7b2393SMatthew Dillon  * place and not all the way back at the beginning.
484ced4470dSMatthew Dillon  *
485ced4470dSMatthew Dillon  * An entry's TID serves as the end_tid for the prior entry
486ced4470dSMatthew Dillon  * So we have to offset the calculation by 1 so that TID falls into
487ced4470dSMatthew Dillon  * the previous entry when populating entries.
488ced4470dSMatthew Dillon  *
489ced4470dSMatthew Dillon  * Because the transaction id space is bursty we need a relatively
490ced4470dSMatthew Dillon  * large number of buckets (like a million) to do a reasonable job
491ced4470dSMatthew Dillon  * for things like an initial bulk mirrors on a very large filesystem.
492e7f926a5SMatthew Dillon  */
493ced4470dSMatthew Dillon #define HIST_COUNT	(1024 * 1024)
4943d7b2393SMatthew Dillon 
495e7f926a5SMatthew Dillon static int
496e7f926a5SMatthew Dillon generate_histogram(int fd, const char *filesystem,
497ced4470dSMatthew Dillon 		   histogram_t *histogram_ary,
49839e88285SMatthew Dillon 		   struct hammer_ioc_mirror_rw *mirror_base,
49939e88285SMatthew Dillon 		   int *repeatp)
500e7f926a5SMatthew Dillon {
501e7f926a5SMatthew Dillon 	struct hammer_ioc_mirror_rw mirror;
5023d7b2393SMatthew Dillon 	union hammer_ioc_mrecord_any *mrec;
503e7f926a5SMatthew Dillon 	hammer_tid_t tid_beg;
504e7f926a5SMatthew Dillon 	hammer_tid_t tid_end;
505ced4470dSMatthew Dillon 	hammer_tid_t tid;
506ced4470dSMatthew Dillon 	hammer_tid_t tidx;
507ced4470dSMatthew Dillon 	u_int64_t *tid_bytes;
5083d7b2393SMatthew Dillon 	u_int64_t total;
5093d7b2393SMatthew Dillon 	u_int64_t accum;
510269cdd19SMatthew Dillon 	int chunkno;
511e7f926a5SMatthew Dillon 	int i;
5123d7b2393SMatthew Dillon 	int res;
5133d7b2393SMatthew Dillon 	int off;
5143d7b2393SMatthew Dillon 	int len;
515e7f926a5SMatthew Dillon 
516e7f926a5SMatthew Dillon 	mirror = *mirror_base;
517e7f926a5SMatthew Dillon 	tid_beg = mirror.tid_beg;
518e7f926a5SMatthew Dillon 	tid_end = mirror.tid_end;
5193d7b2393SMatthew Dillon 	mirror.head.flags |= HAMMER_IOC_MIRROR_NODATA;
520e7f926a5SMatthew Dillon 
5213d7b2393SMatthew Dillon 	if (*histogram_ary == NULL) {
522ced4470dSMatthew Dillon 		*histogram_ary = malloc(sizeof(struct histogram) *
5233d7b2393SMatthew Dillon 					(HIST_COUNT + 2));
5243d7b2393SMatthew Dillon 	}
5253d7b2393SMatthew Dillon 	if (tid_beg >= tid_end)
526e7f926a5SMatthew Dillon 		return(0);
527e7f926a5SMatthew Dillon 
528ced4470dSMatthew Dillon 	/* needs 2 extra */
529ced4470dSMatthew Dillon 	tid_bytes = malloc(sizeof(*tid_bytes) * (HIST_COUNT + 2));
5304839c61eSSascha Wildner 	bzero(tid_bytes, sizeof(*tid_bytes) * (HIST_COUNT + 2));
531ced4470dSMatthew Dillon 
53239e88285SMatthew Dillon 	if (*repeatp == 0) {
5333d7b2393SMatthew Dillon 		fprintf(stderr, "Prescan to break up bulk transfer");
5343d7b2393SMatthew Dillon 		if (VerboseOpt > 1)
5353d7b2393SMatthew Dillon 			fprintf(stderr, " (%juMB chunks)",
5363d7b2393SMatthew Dillon 				(uintmax_t)(SplitupOpt / (1024 * 1024)));
5373d7b2393SMatthew Dillon 		fprintf(stderr, "\n");
53839e88285SMatthew Dillon 	}
539e7f926a5SMatthew Dillon 
540ced4470dSMatthew Dillon 	/*
541ced4470dSMatthew Dillon 	 * Note: (tid_beg,tid_end), range is inclusive of both beg & end.
542ced4470dSMatthew Dillon 	 *
543ced4470dSMatthew Dillon 	 * Note: Estimates can be off when the mirror is way behind due
544ced4470dSMatthew Dillon 	 *	 to skips.
545ced4470dSMatthew Dillon 	 */
5463d7b2393SMatthew Dillon 	total = 0;
5473d7b2393SMatthew Dillon 	accum = 0;
548269cdd19SMatthew Dillon 	chunkno = 0;
5493d7b2393SMatthew Dillon 	for (;;) {
5503d7b2393SMatthew Dillon 		mirror.count = 0;
551e7f926a5SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) {
552e7f926a5SMatthew Dillon 			fprintf(stderr, "Mirror-read %s failed: %s\n",
553e7f926a5SMatthew Dillon 				filesystem, strerror(errno));
554e7f926a5SMatthew Dillon 			exit(1);
555e7f926a5SMatthew Dillon 		}
556e7f926a5SMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) {
557e7f926a5SMatthew Dillon 			fprintf(stderr,
558e7f926a5SMatthew Dillon 				"Mirror-read %s fatal error %d\n",
559e7f926a5SMatthew Dillon 				filesystem, mirror.head.error);
560e7f926a5SMatthew Dillon 			exit(1);
561e7f926a5SMatthew Dillon 		}
5623d7b2393SMatthew Dillon 		for (off = 0;
5633d7b2393SMatthew Dillon 		     off < mirror.count;
5643d7b2393SMatthew Dillon 		     off += HAMMER_HEAD_DOALIGN(mrec->head.rec_size)
5653d7b2393SMatthew Dillon 		) {
5663d7b2393SMatthew Dillon 			mrec = (void *)((char *)mirror.ubuf + off);
567e7f926a5SMatthew Dillon 
568e7f926a5SMatthew Dillon 			/*
569ced4470dSMatthew Dillon 			 * We only care about general RECs and PASS
570ced4470dSMatthew Dillon 			 * records.  We ignore SKIPs.
571e7f926a5SMatthew Dillon 			 */
572ced4470dSMatthew Dillon 			switch (mrec->head.type & HAMMER_MRECF_TYPE_LOMASK) {
573ced4470dSMatthew Dillon 			case HAMMER_MREC_TYPE_REC:
574ced4470dSMatthew Dillon 			case HAMMER_MREC_TYPE_PASS:
575ced4470dSMatthew Dillon 				break;
576ced4470dSMatthew Dillon 			default:
577ced4470dSMatthew Dillon 				continue;
578e7f926a5SMatthew Dillon 			}
5793d7b2393SMatthew Dillon 
580ced4470dSMatthew Dillon 			/*
581ced4470dSMatthew Dillon 			 * Calculate for two indices, create_tid and
582ced4470dSMatthew Dillon 			 * delete_tid.  Record data only applies to
583ced4470dSMatthew Dillon 			 * the create_tid.
584ced4470dSMatthew Dillon 			 *
585ced4470dSMatthew Dillon 			 * When tid is exactly on the boundary it really
586ced4470dSMatthew Dillon 			 * belongs to the previous entry because scans
587ced4470dSMatthew Dillon 			 * are inclusive of the ending entry.
588ced4470dSMatthew Dillon 			 */
589ced4470dSMatthew Dillon 			tid = mrec->rec.leaf.base.delete_tid;
590ced4470dSMatthew Dillon 			if (tid && tid >= tid_beg && tid <= tid_end) {
591ced4470dSMatthew Dillon 				len = HAMMER_HEAD_DOALIGN(mrec->head.rec_size);
592ced4470dSMatthew Dillon 				if (mrec->head.type ==
593ced4470dSMatthew Dillon 				    HAMMER_MREC_TYPE_REC) {
594ced4470dSMatthew Dillon 					len -= HAMMER_HEAD_DOALIGN(
595ced4470dSMatthew Dillon 						    mrec->rec.leaf.data_len);
596ced4470dSMatthew Dillon 					assert(len > 0);
597ced4470dSMatthew Dillon 				}
598ced4470dSMatthew Dillon 				i = (tid - tid_beg) * HIST_COUNT /
5993d7b2393SMatthew Dillon 				    (tid_end - tid_beg);
600ced4470dSMatthew Dillon 				tidx = tid_beg + i * (tid_end - tid_beg) /
601ced4470dSMatthew Dillon 						 HIST_COUNT;
602ced4470dSMatthew Dillon 				if (tid == tidx && i)
603ced4470dSMatthew Dillon 					--i;
604ced4470dSMatthew Dillon 				assert(i >= 0 && i < HIST_COUNT);
6053d7b2393SMatthew Dillon 				tid_bytes[i] += len;
6063d7b2393SMatthew Dillon 				total += len;
6073d7b2393SMatthew Dillon 				accum += len;
6083d7b2393SMatthew Dillon 			}
609ced4470dSMatthew Dillon 
610ced4470dSMatthew Dillon 			tid = mrec->rec.leaf.base.create_tid;
611ced4470dSMatthew Dillon 			if (tid && tid >= tid_beg && tid <= tid_end) {
612ced4470dSMatthew Dillon 				len = HAMMER_HEAD_DOALIGN(mrec->head.rec_size);
613ced4470dSMatthew Dillon 				if (mrec->head.type ==
614ced4470dSMatthew Dillon 				    HAMMER_MREC_TYPE_REC_NODATA) {
615ced4470dSMatthew Dillon 					len += HAMMER_HEAD_DOALIGN(
616ced4470dSMatthew Dillon 						    mrec->rec.leaf.data_len);
617ced4470dSMatthew Dillon 				}
618ced4470dSMatthew Dillon 				i = (tid - tid_beg) * HIST_COUNT /
619ced4470dSMatthew Dillon 				    (tid_end - tid_beg);
620ced4470dSMatthew Dillon 				tidx = tid_beg + i * (tid_end - tid_beg) /
621ced4470dSMatthew Dillon 						 HIST_COUNT;
622ced4470dSMatthew Dillon 				if (tid == tidx && i)
623ced4470dSMatthew Dillon 					--i;
624ced4470dSMatthew Dillon 				assert(i >= 0 && i < HIST_COUNT);
625ced4470dSMatthew Dillon 				tid_bytes[i] += len;
626ced4470dSMatthew Dillon 				total += len;
627ced4470dSMatthew Dillon 				accum += len;
6283d7b2393SMatthew Dillon 			}
6293d7b2393SMatthew Dillon 		}
63039e88285SMatthew Dillon 		if (*repeatp == 0 && accum > SplitupOpt) {
631269cdd19SMatthew Dillon 			if (VerboseOpt > 1) {
6323d7b2393SMatthew Dillon 				fprintf(stderr, ".");
6333d7b2393SMatthew Dillon 				fflush(stderr);
6343d7b2393SMatthew Dillon 			}
635269cdd19SMatthew Dillon 			++chunkno;
636269cdd19SMatthew Dillon 			score_printf(LINE2, "Prescan chunk %d", chunkno);
637269cdd19SMatthew Dillon 			accum = 0;
6383d7b2393SMatthew Dillon 		}
6393d7b2393SMatthew Dillon 		if (mirror.count == 0)
6403d7b2393SMatthew Dillon 			break;
6413d7b2393SMatthew Dillon 		mirror.key_beg = mirror.key_cur;
6423d7b2393SMatthew Dillon 	}
6433d7b2393SMatthew Dillon 
6443d7b2393SMatthew Dillon 	/*
645d6c40a21SFrançois Tigeot 	 * Reduce to SplitupOpt (default 4GB) chunks.  This code may
646ced4470dSMatthew Dillon 	 * use up to two additional elements.  Do the array in-place.
647ced4470dSMatthew Dillon 	 *
648ced4470dSMatthew Dillon 	 * Inefficient degenerate cases can occur if we do not accumulate
649ced4470dSMatthew Dillon 	 * at least the requested split amount, so error on the side of
650ced4470dSMatthew Dillon 	 * going over a bit.
6513d7b2393SMatthew Dillon 	 */
6523d7b2393SMatthew Dillon 	res = 0;
653ced4470dSMatthew Dillon 	(*histogram_ary)[res].tid = tid_beg;
654ced4470dSMatthew Dillon 	(*histogram_ary)[res].bytes = tid_bytes[0];
655ced4470dSMatthew Dillon 	for (i = 1; i < HIST_COUNT; ++i) {
656ced4470dSMatthew Dillon 		if ((*histogram_ary)[res].bytes >= SplitupOpt) {
657ced4470dSMatthew Dillon 			++res;
658ced4470dSMatthew Dillon 			(*histogram_ary)[res].tid = tid_beg +
6593d7b2393SMatthew Dillon 					i * (tid_end - tid_beg) /
6603d7b2393SMatthew Dillon 					HIST_COUNT;
661ced4470dSMatthew Dillon 			(*histogram_ary)[res].bytes = 0;
662ced4470dSMatthew Dillon 
6633d7b2393SMatthew Dillon 		}
664ced4470dSMatthew Dillon 		(*histogram_ary)[res].bytes += tid_bytes[i];
6653d7b2393SMatthew Dillon 	}
666ced4470dSMatthew Dillon 	++res;
667ced4470dSMatthew Dillon 	(*histogram_ary)[res].tid = tid_end;
668ced4470dSMatthew Dillon 	(*histogram_ary)[res].bytes = -1;
669ced4470dSMatthew Dillon 
67039e88285SMatthew Dillon 	if (*repeatp == 0) {
6713d7b2393SMatthew Dillon 		if (VerboseOpt > 1)
6723d7b2393SMatthew Dillon 			fprintf(stderr, "\n");	/* newline after ... */
673269cdd19SMatthew Dillon 		score_printf(LINE3, "Prescan %d chunks, total %ju MBytes",
674269cdd19SMatthew Dillon 			res, (uintmax_t)total / (1024 * 1024));
67527eff55eSMatthew Dillon 		fprintf(stderr, "Prescan %d chunks, total %ju MBytes (",
6763d7b2393SMatthew Dillon 			res, (uintmax_t)total / (1024 * 1024));
677ced4470dSMatthew Dillon 		for (i = 0; i < res && i < 3; ++i) {
678ced4470dSMatthew Dillon 			if (i)
679ced4470dSMatthew Dillon 				fprintf(stderr, ", ");
68039e88285SMatthew Dillon 			fprintf(stderr, "%ju",
68139e88285SMatthew Dillon 				(uintmax_t)(*histogram_ary)[i].bytes);
682ced4470dSMatthew Dillon 		}
683ced4470dSMatthew Dillon 		if (i < res)
684ced4470dSMatthew Dillon 			fprintf(stderr, ", ...");
685ced4470dSMatthew Dillon 		fprintf(stderr, ")\n");
68639e88285SMatthew Dillon 	}
68739e88285SMatthew Dillon 	assert(res <= HIST_COUNT);
68839e88285SMatthew Dillon 	*repeatp = 1;
689ced4470dSMatthew Dillon 
690ced4470dSMatthew Dillon 	free(tid_bytes);
6913d7b2393SMatthew Dillon 	return(res);
692e7f926a5SMatthew Dillon }
693e7f926a5SMatthew Dillon 
69401a72c9fSMatthew Dillon static void
69501a72c9fSMatthew Dillon create_pfs(const char *filesystem, uuid_t *s_uuid)
69601a72c9fSMatthew Dillon {
697f414d101SSascha Wildner 	if (ForceYesOpt == 1) {
69807485271SMichael Neumann 		fprintf(stderr, "PFS slave %s does not exist. "
69907485271SMichael Neumann 			"Auto create new slave PFS!\n", filesystem);
70007485271SMichael Neumann 
701f414d101SSascha Wildner 	} else {
70201a72c9fSMatthew Dillon 		fprintf(stderr, "PFS slave %s does not exist.\n"
70301a72c9fSMatthew Dillon 			"Do you want to create a new slave PFS? (yes|no) ",
70401a72c9fSMatthew Dillon 			filesystem);
70501a72c9fSMatthew Dillon 		fflush(stderr);
70601a72c9fSMatthew Dillon 		if (getyn() != 1) {
70701a72c9fSMatthew Dillon 			fprintf(stderr, "Aborting operation\n");
70801a72c9fSMatthew Dillon 			exit(1);
70901a72c9fSMatthew Dillon 		}
71007485271SMichael Neumann 	}
71101a72c9fSMatthew Dillon 
71201a72c9fSMatthew Dillon 	u_int32_t status;
71301a72c9fSMatthew Dillon 	char *shared_uuid = NULL;
71401a72c9fSMatthew Dillon 	uuid_to_string(s_uuid, &shared_uuid, &status);
71501a72c9fSMatthew Dillon 
71601a72c9fSMatthew Dillon 	char *cmd = NULL;
71701a72c9fSMatthew Dillon 	asprintf(&cmd, "/sbin/hammer pfs-slave '%s' shared-uuid=%s 1>&2",
71801a72c9fSMatthew Dillon 		 filesystem, shared_uuid);
71901a72c9fSMatthew Dillon 	free(shared_uuid);
72001a72c9fSMatthew Dillon 
72101a72c9fSMatthew Dillon 	if (cmd == NULL) {
72201a72c9fSMatthew Dillon 		fprintf(stderr, "Failed to alloc memory\n");
72301a72c9fSMatthew Dillon 		exit(1);
72401a72c9fSMatthew Dillon 	}
72501a72c9fSMatthew Dillon 	if (system(cmd) != 0) {
72601a72c9fSMatthew Dillon 		fprintf(stderr, "Failed to create PFS\n");
72701a72c9fSMatthew Dillon 	}
72801a72c9fSMatthew Dillon 	free(cmd);
72901a72c9fSMatthew Dillon }
73001a72c9fSMatthew Dillon 
73117dd83bcSMatthew Dillon /*
73217dd83bcSMatthew Dillon  * Pipe the mirroring data stream on stdin to the HAMMER VFS, adding
73317dd83bcSMatthew Dillon  * some additional packet types to negotiate TID ranges and to verify
73417dd83bcSMatthew Dillon  * completion.  The HAMMER VFS does most of the work.
73517dd83bcSMatthew Dillon  *
73617dd83bcSMatthew Dillon  * It is important to note that the mirror.key_{beg,end} range must
73717dd83bcSMatthew Dillon  * match the ranged used by the original.  For now both sides use
73817dd83bcSMatthew Dillon  * range the entire key space.
73917dd83bcSMatthew Dillon  *
74017dd83bcSMatthew Dillon  * It is even more important that the records in the stream conform
74117dd83bcSMatthew Dillon  * to the TID range also supplied in the stream.  The HAMMER VFS will
74217dd83bcSMatthew Dillon  * use the REC, PASS, and SKIP record types to track the portions of
74317dd83bcSMatthew Dillon  * the B-Tree being scanned in order to be able to proactively delete
74417dd83bcSMatthew Dillon  * records on the target within those active areas that are not mentioned
74517dd83bcSMatthew Dillon  * by the source.
74617dd83bcSMatthew Dillon  *
74717dd83bcSMatthew Dillon  * The mirror.key_cur field is used by the VFS to do this tracking.  It
74817dd83bcSMatthew Dillon  * must be initialized to key_beg but then is persistently updated by
74917dd83bcSMatthew Dillon  * the HAMMER VFS on each successive ioctl() call.  If you blow up this
75017dd83bcSMatthew Dillon  * field you will blow up the mirror target, possibly to the point of
75117dd83bcSMatthew Dillon  * deleting everything.  As a safety measure the HAMMER VFS simply marks
75217dd83bcSMatthew Dillon  * the records that the source has destroyed as deleted on the target,
75317dd83bcSMatthew Dillon  * and normal pruning operations will deal with their final disposition
75417dd83bcSMatthew Dillon  * at some later time.
75517dd83bcSMatthew Dillon  */
756a7fbbf91SMatthew Dillon void
757a7fbbf91SMatthew Dillon hammer_cmd_mirror_write(char **av, int ac)
758a7fbbf91SMatthew Dillon {
759a7fbbf91SMatthew Dillon 	struct hammer_ioc_mirror_rw mirror;
760a7fbbf91SMatthew Dillon 	const char *filesystem;
761a7fbbf91SMatthew Dillon 	char *buf = malloc(SERIALBUF_SIZE);
762d4e5b69bSMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
76317dd83bcSMatthew Dillon 	struct hammer_ioc_mrecord_head pickup;
764243ca327SMatthew Dillon 	struct hammer_ioc_synctid synctid;
76517dd83bcSMatthew Dillon 	union hammer_ioc_mrecord_any mrec_tmp;
76617dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
76701a72c9fSMatthew Dillon 	struct stat st;
768243ca327SMatthew Dillon 	int error;
769243ca327SMatthew Dillon 	int fd;
77048eadef9SMatthew Dillon 	int n;
771a7fbbf91SMatthew Dillon 
77234bb69d8SThomas Nikolajsen 	if (ac != 1)
773a7fbbf91SMatthew Dillon 		mirror_usage(1);
774a7fbbf91SMatthew Dillon 	filesystem = av[0];
775*69f5a58cSMatthew Dillon 	hammer_check_restrict(filesystem);
776a7fbbf91SMatthew Dillon 
77748eadef9SMatthew Dillon 	pickup.signature = 0;
77848eadef9SMatthew Dillon 	pickup.type = 0;
77948eadef9SMatthew Dillon 
78048eadef9SMatthew Dillon again:
781a7fbbf91SMatthew Dillon 	bzero(&mirror, sizeof(mirror));
782a7fbbf91SMatthew Dillon 	hammer_key_beg_init(&mirror.key_beg);
783a7fbbf91SMatthew Dillon 	hammer_key_end_init(&mirror.key_end);
78417dd83bcSMatthew Dillon 	mirror.key_end = mirror.key_beg;
785a7fbbf91SMatthew Dillon 
78601a72c9fSMatthew Dillon 	/*
78701a72c9fSMatthew Dillon 	 * Read initial packet
78801a72c9fSMatthew Dillon 	 */
78901a72c9fSMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
79001a72c9fSMatthew Dillon 	if (mrec == NULL) {
79101a72c9fSMatthew Dillon 		if (error == 0)
79201a72c9fSMatthew Dillon 			fprintf(stderr, "validate_mrec_header: short read\n");
79301a72c9fSMatthew Dillon 		exit(1);
79401a72c9fSMatthew Dillon 	}
79501a72c9fSMatthew Dillon 	/*
79601a72c9fSMatthew Dillon 	 * Validate packet
79701a72c9fSMatthew Dillon 	 */
79801a72c9fSMatthew Dillon 	if (mrec->head.type == HAMMER_MREC_TYPE_TERM) {
79901a72c9fSMatthew Dillon 		return;
80001a72c9fSMatthew Dillon 	}
80101a72c9fSMatthew Dillon 	if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) {
80201a72c9fSMatthew Dillon 		fprintf(stderr, "validate_mrec_header: did not get expected "
80301a72c9fSMatthew Dillon 				"PFSD record type\n");
80401a72c9fSMatthew Dillon 		exit(1);
80501a72c9fSMatthew Dillon 	}
80601a72c9fSMatthew Dillon 	if (mrec->head.rec_size != sizeof(mrec->pfs)) {
80701a72c9fSMatthew Dillon 		fprintf(stderr, "validate_mrec_header: unexpected payload "
80801a72c9fSMatthew Dillon 				"size\n");
80901a72c9fSMatthew Dillon 		exit(1);
81001a72c9fSMatthew Dillon 	}
81101a72c9fSMatthew Dillon 
81201a72c9fSMatthew Dillon 	/*
81301a72c9fSMatthew Dillon 	 * Create slave PFS if it doesn't yet exist
81401a72c9fSMatthew Dillon 	 */
81501a72c9fSMatthew Dillon 	if (lstat(filesystem, &st) != 0) {
81601a72c9fSMatthew Dillon 		create_pfs(filesystem, &mrec->pfs.pfsd.shared_uuid);
81701a72c9fSMatthew Dillon 	}
81801a72c9fSMatthew Dillon 	free(mrec);
81901a72c9fSMatthew Dillon 	mrec = NULL;
82001a72c9fSMatthew Dillon 
821d4e5b69bSMatthew Dillon 	fd = getpfs(&pfs, filesystem);
822a7fbbf91SMatthew Dillon 
823243ca327SMatthew Dillon 	/*
824d4e5b69bSMatthew Dillon 	 * In two-way mode the target writes out a PFS packet first.
825d4e5b69bSMatthew Dillon 	 * The source uses our tid_end as its tid_beg by default,
826d4e5b69bSMatthew Dillon 	 * picking up where it left off.
827243ca327SMatthew Dillon 	 */
828d4e5b69bSMatthew Dillon 	mirror.tid_beg = 0;
829d4e5b69bSMatthew Dillon 	if (TwoWayPipeOpt) {
830e7f926a5SMatthew Dillon 		generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp);
831e7f926a5SMatthew Dillon 		if (mirror.tid_beg < mrec_tmp.pfs.pfsd.sync_beg_tid)
832e7f926a5SMatthew Dillon 			mirror.tid_beg = mrec_tmp.pfs.pfsd.sync_beg_tid;
833e7f926a5SMatthew Dillon 		mirror.tid_end = mrec_tmp.pfs.pfsd.sync_end_tid;
834e7f926a5SMatthew Dillon 		write_mrecord(1, HAMMER_MREC_TYPE_PFSD,
835e7f926a5SMatthew Dillon 			      &mrec_tmp, sizeof(mrec_tmp.pfs));
836d4e5b69bSMatthew Dillon 	}
837d4e5b69bSMatthew Dillon 
838d4e5b69bSMatthew Dillon 	/*
83917dd83bcSMatthew Dillon 	 * Read and process the PFS header.  The source informs us of
84017dd83bcSMatthew Dillon 	 * the TID range the stream represents.
841d4e5b69bSMatthew Dillon 	 */
84248eadef9SMatthew Dillon 	n = validate_mrec_header(fd, 0, 1, pfs.pfs_id, &pickup,
843d4e5b69bSMatthew Dillon 				 &mirror.tid_beg, &mirror.tid_end);
84448eadef9SMatthew Dillon 	if (n < 0) {	/* got TERM record */
84548eadef9SMatthew Dillon 		relpfs(fd, &pfs);
84648eadef9SMatthew Dillon 		return;
84748eadef9SMatthew Dillon 	}
84834ebae70SMatthew Dillon 
849a7fbbf91SMatthew Dillon 	mirror.ubuf = buf;
850a7fbbf91SMatthew Dillon 	mirror.size = SERIALBUF_SIZE;
851a7fbbf91SMatthew Dillon 
852243ca327SMatthew Dillon 	/*
85317dd83bcSMatthew Dillon 	 * Read and process bulk records (REC, PASS, and SKIP types).
85417dd83bcSMatthew Dillon 	 *
85517dd83bcSMatthew Dillon 	 * On your life, do NOT mess with mirror.key_cur or your mirror
85617dd83bcSMatthew Dillon 	 * target may become history.
857243ca327SMatthew Dillon 	 */
858a7fbbf91SMatthew Dillon 	for (;;) {
859a7fbbf91SMatthew Dillon 		mirror.count = 0;
860d4e5b69bSMatthew Dillon 		mirror.pfs_id = pfs.pfs_id;
861d4e5b69bSMatthew Dillon 		mirror.shared_uuid = pfs.ondisk->shared_uuid;
862a7fbbf91SMatthew Dillon 		mirror.size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup);
863a7fbbf91SMatthew Dillon 		if (mirror.size <= 0)
864a7fbbf91SMatthew Dillon 			break;
865a7fbbf91SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_MIRROR_WRITE, &mirror) < 0) {
866a7fbbf91SMatthew Dillon 			fprintf(stderr, "Mirror-write %s failed: %s\n",
867a7fbbf91SMatthew Dillon 				filesystem, strerror(errno));
868a7fbbf91SMatthew Dillon 			exit(1);
869a7fbbf91SMatthew Dillon 		}
87017dd83bcSMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) {
87117dd83bcSMatthew Dillon 			fprintf(stderr,
87217dd83bcSMatthew Dillon 				"Mirror-write %s fatal error %d\n",
87317dd83bcSMatthew Dillon 				filesystem, mirror.head.error);
87417dd83bcSMatthew Dillon 			exit(1);
87517dd83bcSMatthew Dillon 		}
876243ca327SMatthew Dillon #if 0
877a7fbbf91SMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_INTR) {
878a7fbbf91SMatthew Dillon 			fprintf(stderr,
879a7fbbf91SMatthew Dillon 				"Mirror-write %s interrupted by timer at"
880243ca327SMatthew Dillon 				" %016llx\n",
881a7fbbf91SMatthew Dillon 				filesystem,
882243ca327SMatthew Dillon 				mirror.key_cur.obj_id);
883a7fbbf91SMatthew Dillon 			exit(0);
884a7fbbf91SMatthew Dillon 		}
885243ca327SMatthew Dillon #endif
886a7fbbf91SMatthew Dillon 	}
887243ca327SMatthew Dillon 
888243ca327SMatthew Dillon 	/*
889243ca327SMatthew Dillon 	 * Read and process the termination sync record.
890243ca327SMatthew Dillon 	 */
891243ca327SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
8929dc76cb1SMatthew Dillon 
8939dc76cb1SMatthew Dillon 	if (mrec && mrec->head.type == HAMMER_MREC_TYPE_TERM) {
89448eadef9SMatthew Dillon 		fprintf(stderr, "Mirror-write: received termination request\n");
89548eadef9SMatthew Dillon 		free(mrec);
8969dc76cb1SMatthew Dillon 		return;
8979dc76cb1SMatthew Dillon 	}
8989dc76cb1SMatthew Dillon 
89917dd83bcSMatthew Dillon 	if (mrec == NULL ||
90048eadef9SMatthew Dillon 	    (mrec->head.type != HAMMER_MREC_TYPE_SYNC &&
90148eadef9SMatthew Dillon 	     mrec->head.type != HAMMER_MREC_TYPE_IDLE) ||
90217dd83bcSMatthew Dillon 	    mrec->head.rec_size != sizeof(mrec->sync)) {
903243ca327SMatthew Dillon 		fprintf(stderr, "Mirror-write %s: Did not get termination "
90417dd83bcSMatthew Dillon 				"sync record, or rec_size is wrong rt=%d\n",
905db7dd3cfSMatthew Dillon 				filesystem,
906a446fca6SSascha Wildner 				(mrec ? (int)mrec->head.type : -1));
907da44aa75SMatthew Dillon 		exit(1);
908243ca327SMatthew Dillon 	}
909243ca327SMatthew Dillon 
910243ca327SMatthew Dillon 	/*
911243ca327SMatthew Dillon 	 * Update the PFS info on the target so the user has visibility
91248eadef9SMatthew Dillon 	 * into the new snapshot, and sync the target filesystem.
913243ca327SMatthew Dillon 	 */
91448eadef9SMatthew Dillon 	if (mrec->head.type == HAMMER_MREC_TYPE_SYNC) {
915d4e5b69bSMatthew Dillon 		update_pfs_snapshot(fd, mirror.tid_end, pfs.pfs_id);
916243ca327SMatthew Dillon 
917243ca327SMatthew Dillon 		bzero(&synctid, sizeof(synctid));
918243ca327SMatthew Dillon 		synctid.op = HAMMER_SYNCTID_SYNC2;
919243ca327SMatthew Dillon 		ioctl(fd, HAMMERIOC_SYNCTID, &synctid);
920243ca327SMatthew Dillon 
92148eadef9SMatthew Dillon 		if (VerboseOpt >= 2) {
92248eadef9SMatthew Dillon 			fprintf(stderr, "Mirror-write %s: succeeded\n",
92348eadef9SMatthew Dillon 				filesystem);
92448eadef9SMatthew Dillon 		}
92548eadef9SMatthew Dillon 	}
92648eadef9SMatthew Dillon 
92748eadef9SMatthew Dillon 	free(mrec);
92848eadef9SMatthew Dillon 	mrec = NULL;
929243ca327SMatthew Dillon 
930243ca327SMatthew Dillon 	/*
931243ca327SMatthew Dillon 	 * Report back to the originator.
932243ca327SMatthew Dillon 	 */
933243ca327SMatthew Dillon 	if (TwoWayPipeOpt) {
93417dd83bcSMatthew Dillon 		mrec_tmp.update.tid = mirror.tid_end;
935243ca327SMatthew Dillon 		write_mrecord(1, HAMMER_MREC_TYPE_UPDATE,
93617dd83bcSMatthew Dillon 			      &mrec_tmp, sizeof(mrec_tmp.update));
937243ca327SMatthew Dillon 	} else {
938a276dc6bSMatthew Dillon 		printf("Source can update synctid to 0x%016jx\n",
939a276dc6bSMatthew Dillon 		       (uintmax_t)mirror.tid_end);
940243ca327SMatthew Dillon 	}
94148eadef9SMatthew Dillon 	relpfs(fd, &pfs);
94248eadef9SMatthew Dillon 	goto again;
943243ca327SMatthew Dillon }
944243ca327SMatthew Dillon 
945243ca327SMatthew Dillon void
946243ca327SMatthew Dillon hammer_cmd_mirror_dump(void)
947243ca327SMatthew Dillon {
948243ca327SMatthew Dillon 	char *buf = malloc(SERIALBUF_SIZE);
94917dd83bcSMatthew Dillon 	struct hammer_ioc_mrecord_head pickup;
95017dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
951243ca327SMatthew Dillon 	int error;
952243ca327SMatthew Dillon 	int size;
95317dd83bcSMatthew Dillon 	int offset;
95417dd83bcSMatthew Dillon 	int bytes;
955243ca327SMatthew Dillon 
956243ca327SMatthew Dillon 	/*
957243ca327SMatthew Dillon 	 * Read and process the PFS header
958243ca327SMatthew Dillon 	 */
959243ca327SMatthew Dillon 	pickup.signature = 0;
960243ca327SMatthew Dillon 	pickup.type = 0;
961243ca327SMatthew Dillon 
962243ca327SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
963243ca327SMatthew Dillon 
96439e88285SMatthew Dillon again:
965243ca327SMatthew Dillon 	/*
966243ca327SMatthew Dillon 	 * Read and process bulk records
967243ca327SMatthew Dillon 	 */
968243ca327SMatthew Dillon 	for (;;) {
969243ca327SMatthew Dillon 		size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup);
970243ca327SMatthew Dillon 		if (size <= 0)
971243ca327SMatthew Dillon 			break;
97217dd83bcSMatthew Dillon 		offset = 0;
97317dd83bcSMatthew Dillon 		while (offset < size) {
97417dd83bcSMatthew Dillon 			mrec = (void *)((char *)buf + offset);
97517dd83bcSMatthew Dillon 			bytes = HAMMER_HEAD_DOALIGN(mrec->head.rec_size);
97617dd83bcSMatthew Dillon 			if (offset + bytes > size) {
97717dd83bcSMatthew Dillon 				fprintf(stderr, "Misaligned record\n");
97817dd83bcSMatthew Dillon 				exit(1);
97917dd83bcSMatthew Dillon 			}
98017dd83bcSMatthew Dillon 
98185a8e8a7SMatthew Dillon 			switch(mrec->head.type & HAMMER_MRECF_TYPE_MASK) {
98285a8e8a7SMatthew Dillon 			case HAMMER_MREC_TYPE_REC_BADCRC:
98317dd83bcSMatthew Dillon 			case HAMMER_MREC_TYPE_REC:
984a276dc6bSMatthew Dillon 				printf("Record obj=%016jx key=%016jx "
98585a8e8a7SMatthew Dillon 				       "rt=%02x ot=%02x",
986a276dc6bSMatthew Dillon 					(uintmax_t)mrec->rec.leaf.base.obj_id,
987a276dc6bSMatthew Dillon 					(uintmax_t)mrec->rec.leaf.base.key,
98817dd83bcSMatthew Dillon 					mrec->rec.leaf.base.rec_type,
98917dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_type);
99085a8e8a7SMatthew Dillon 				if (mrec->head.type ==
99185a8e8a7SMatthew Dillon 				    HAMMER_MREC_TYPE_REC_BADCRC) {
99285a8e8a7SMatthew Dillon 					printf(" (BAD CRC)");
99385a8e8a7SMatthew Dillon 				}
99485a8e8a7SMatthew Dillon 				printf("\n");
995a276dc6bSMatthew Dillon 				printf("       tids %016jx:%016jx data=%d\n",
996a276dc6bSMatthew Dillon 				    (uintmax_t)mrec->rec.leaf.base.create_tid,
997a276dc6bSMatthew Dillon 				    (uintmax_t)mrec->rec.leaf.base.delete_tid,
99817dd83bcSMatthew Dillon 				    mrec->rec.leaf.data_len);
99917dd83bcSMatthew Dillon 				break;
100017dd83bcSMatthew Dillon 			case HAMMER_MREC_TYPE_PASS:
1001a276dc6bSMatthew Dillon 				printf("Pass   obj=%016jx key=%016jx "
100217dd83bcSMatthew Dillon 				       "rt=%02x ot=%02x\n",
1003a276dc6bSMatthew Dillon 					(uintmax_t)mrec->rec.leaf.base.obj_id,
1004a276dc6bSMatthew Dillon 					(uintmax_t)mrec->rec.leaf.base.key,
100517dd83bcSMatthew Dillon 					mrec->rec.leaf.base.rec_type,
100617dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_type);
1007a276dc6bSMatthew Dillon 				printf("       tids %016jx:%016jx data=%d\n",
1008a276dc6bSMatthew Dillon 				    (uintmax_t)mrec->rec.leaf.base.create_tid,
1009a276dc6bSMatthew Dillon 				    (uintmax_t)mrec->rec.leaf.base.delete_tid,
101017dd83bcSMatthew Dillon 					mrec->rec.leaf.data_len);
101117dd83bcSMatthew Dillon 				break;
101217dd83bcSMatthew Dillon 			case HAMMER_MREC_TYPE_SKIP:
1013a276dc6bSMatthew Dillon 				printf("Skip   obj=%016jx key=%016jx rt=%02x to\n"
1014a276dc6bSMatthew Dillon 				       "       obj=%016jx key=%016jx rt=%02x\n",
1015a276dc6bSMatthew Dillon 				       (uintmax_t)mrec->skip.skip_beg.obj_id,
1016a276dc6bSMatthew Dillon 				       (uintmax_t)mrec->skip.skip_beg.key,
101717dd83bcSMatthew Dillon 				       mrec->skip.skip_beg.rec_type,
1018a276dc6bSMatthew Dillon 				       (uintmax_t)mrec->skip.skip_end.obj_id,
1019a276dc6bSMatthew Dillon 				       (uintmax_t)mrec->skip.skip_end.key,
102017dd83bcSMatthew Dillon 				       mrec->skip.skip_end.rec_type);
102117dd83bcSMatthew Dillon 			default:
102217dd83bcSMatthew Dillon 				break;
102317dd83bcSMatthew Dillon 			}
102417dd83bcSMatthew Dillon 			offset += bytes;
1025243ca327SMatthew Dillon 		}
1026243ca327SMatthew Dillon 	}
1027243ca327SMatthew Dillon 
1028243ca327SMatthew Dillon 	/*
1029243ca327SMatthew Dillon 	 * Read and process the termination sync record.
1030243ca327SMatthew Dillon 	 */
1031243ca327SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
103248eadef9SMatthew Dillon 	if (mrec == NULL ||
103348eadef9SMatthew Dillon 	    (mrec->head.type != HAMMER_MREC_TYPE_SYNC &&
103448eadef9SMatthew Dillon 	     mrec->head.type != HAMMER_MREC_TYPE_IDLE)
103548eadef9SMatthew Dillon 	 ) {
1036243ca327SMatthew Dillon 		fprintf(stderr, "Mirror-dump: Did not get termination "
1037243ca327SMatthew Dillon 				"sync record\n");
1038243ca327SMatthew Dillon 	}
103939e88285SMatthew Dillon 
104039e88285SMatthew Dillon 	/*
104139e88285SMatthew Dillon 	 * Continue with more batches until EOF.
104239e88285SMatthew Dillon 	 */
104339e88285SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
104439e88285SMatthew Dillon 	if (mrec)
104539e88285SMatthew Dillon 		goto again;
1046a7fbbf91SMatthew Dillon }
1047a7fbbf91SMatthew Dillon 
1048a7fbbf91SMatthew Dillon void
104948eadef9SMatthew Dillon hammer_cmd_mirror_copy(char **av, int ac, int streaming)
1050a7fbbf91SMatthew Dillon {
105134ebae70SMatthew Dillon 	pid_t pid1;
105234ebae70SMatthew Dillon 	pid_t pid2;
105334ebae70SMatthew Dillon 	int fds[2];
105408b71f9fSMatthew Dillon 	const char *xav[32];
1055243ca327SMatthew Dillon 	char tbuf[16];
105634ebae70SMatthew Dillon 	char *ptr;
1057243ca327SMatthew Dillon 	int xac;
105834ebae70SMatthew Dillon 
105934ebae70SMatthew Dillon 	if (ac != 2)
106034ebae70SMatthew Dillon 		mirror_usage(1);
106134ebae70SMatthew Dillon 
1062e7f926a5SMatthew Dillon 	TwoWayPipeOpt = 1;
10630bd7a37cSMatthew Dillon 	signal(SIGPIPE, SIG_IGN);
1064e7f926a5SMatthew Dillon 
1065e7f926a5SMatthew Dillon again:
106634ebae70SMatthew Dillon 	if (pipe(fds) < 0) {
106734ebae70SMatthew Dillon 		perror("pipe");
106834ebae70SMatthew Dillon 		exit(1);
106934ebae70SMatthew Dillon 	}
107034ebae70SMatthew Dillon 
107134ebae70SMatthew Dillon 	/*
107234ebae70SMatthew Dillon 	 * Source
107334ebae70SMatthew Dillon 	 */
107434ebae70SMatthew Dillon 	if ((pid1 = fork()) == 0) {
10750bd7a37cSMatthew Dillon 		signal(SIGPIPE, SIG_DFL);
107634ebae70SMatthew Dillon 		dup2(fds[0], 0);
107734ebae70SMatthew Dillon 		dup2(fds[0], 1);
107834ebae70SMatthew Dillon 		close(fds[0]);
107934ebae70SMatthew Dillon 		close(fds[1]);
108034ebae70SMatthew Dillon 		if ((ptr = strchr(av[0], ':')) != NULL) {
108134ebae70SMatthew Dillon 			*ptr++ = 0;
1082243ca327SMatthew Dillon 			xac = 0;
1083243ca327SMatthew Dillon 			xav[xac++] = "ssh";
10843a998207SMatthew Dillon 			if (CompressOpt)
10853a998207SMatthew Dillon 				xav[xac++] = "-C";
10866c45ca3eSMatthew Dillon 			if (SshPort) {
10876c45ca3eSMatthew Dillon 				xav[xac++] = "-p";
10886c45ca3eSMatthew Dillon 				xav[xac++] = SshPort;
10896c45ca3eSMatthew Dillon 			}
1090243ca327SMatthew Dillon 			xav[xac++] = av[0];
1091243ca327SMatthew Dillon 			xav[xac++] = "hammer";
109248eadef9SMatthew Dillon 
109348eadef9SMatthew Dillon 			switch(VerboseOpt) {
109448eadef9SMatthew Dillon 			case 0:
109548eadef9SMatthew Dillon 				break;
109648eadef9SMatthew Dillon 			case 1:
1097243ca327SMatthew Dillon 				xav[xac++] = "-v";
109848eadef9SMatthew Dillon 				break;
109948eadef9SMatthew Dillon 			case 2:
110048eadef9SMatthew Dillon 				xav[xac++] = "-vv";
110148eadef9SMatthew Dillon 				break;
110248eadef9SMatthew Dillon 			default:
110348eadef9SMatthew Dillon 				xav[xac++] = "-vvv";
110448eadef9SMatthew Dillon 				break;
110548eadef9SMatthew Dillon 			}
110607485271SMichael Neumann 			if (ForceYesOpt) {
110707485271SMichael Neumann 				xav[xac++] = "-y";
110807485271SMichael Neumann 			}
1109243ca327SMatthew Dillon 			xav[xac++] = "-2";
1110243ca327SMatthew Dillon 			if (TimeoutOpt) {
1111243ca327SMatthew Dillon 				snprintf(tbuf, sizeof(tbuf), "%d", TimeoutOpt);
1112243ca327SMatthew Dillon 				xav[xac++] = "-t";
1113243ca327SMatthew Dillon 				xav[xac++] = tbuf;
1114243ca327SMatthew Dillon 			}
111508b71f9fSMatthew Dillon 			if (SplitupOptStr) {
111608b71f9fSMatthew Dillon 				xav[xac++] = "-S";
111708b71f9fSMatthew Dillon 				xav[xac++] = SplitupOptStr;
111808b71f9fSMatthew Dillon 			}
111948eadef9SMatthew Dillon 			if (streaming)
1120901f434aSMatthew Dillon 				xav[xac++] = "mirror-read-stream";
112148eadef9SMatthew Dillon 			else
1122243ca327SMatthew Dillon 				xav[xac++] = "mirror-read";
1123243ca327SMatthew Dillon 			xav[xac++] = ptr;
1124243ca327SMatthew Dillon 			xav[xac++] = NULL;
1125243ca327SMatthew Dillon 			execv("/usr/bin/ssh", (void *)xav);
112634ebae70SMatthew Dillon 		} else {
112748eadef9SMatthew Dillon 			hammer_cmd_mirror_read(av, 1, streaming);
1128243ca327SMatthew Dillon 			fflush(stdout);
1129243ca327SMatthew Dillon 			fflush(stderr);
113034ebae70SMatthew Dillon 		}
113153d93cc7SMatthew Dillon 		_exit(1);
113234ebae70SMatthew Dillon 	}
113334ebae70SMatthew Dillon 
113434ebae70SMatthew Dillon 	/*
113534ebae70SMatthew Dillon 	 * Target
113634ebae70SMatthew Dillon 	 */
113734ebae70SMatthew Dillon 	if ((pid2 = fork()) == 0) {
11380bd7a37cSMatthew Dillon 		signal(SIGPIPE, SIG_DFL);
113934ebae70SMatthew Dillon 		dup2(fds[1], 0);
114034ebae70SMatthew Dillon 		dup2(fds[1], 1);
114134ebae70SMatthew Dillon 		close(fds[0]);
114234ebae70SMatthew Dillon 		close(fds[1]);
114334ebae70SMatthew Dillon 		if ((ptr = strchr(av[1], ':')) != NULL) {
114434ebae70SMatthew Dillon 			*ptr++ = 0;
1145243ca327SMatthew Dillon 			xac = 0;
1146243ca327SMatthew Dillon 			xav[xac++] = "ssh";
11473a998207SMatthew Dillon 			if (CompressOpt)
11483a998207SMatthew Dillon 				xav[xac++] = "-C";
11496c45ca3eSMatthew Dillon 			if (SshPort) {
11506c45ca3eSMatthew Dillon 				xav[xac++] = "-p";
11516c45ca3eSMatthew Dillon 				xav[xac++] = SshPort;
11526c45ca3eSMatthew Dillon 			}
1153243ca327SMatthew Dillon 			xav[xac++] = av[1];
1154243ca327SMatthew Dillon 			xav[xac++] = "hammer";
115548eadef9SMatthew Dillon 
115648eadef9SMatthew Dillon 			switch(VerboseOpt) {
115748eadef9SMatthew Dillon 			case 0:
115848eadef9SMatthew Dillon 				break;
115948eadef9SMatthew Dillon 			case 1:
1160243ca327SMatthew Dillon 				xav[xac++] = "-v";
116148eadef9SMatthew Dillon 				break;
116248eadef9SMatthew Dillon 			case 2:
116348eadef9SMatthew Dillon 				xav[xac++] = "-vv";
116448eadef9SMatthew Dillon 				break;
116548eadef9SMatthew Dillon 			default:
116648eadef9SMatthew Dillon 				xav[xac++] = "-vvv";
116748eadef9SMatthew Dillon 				break;
116848eadef9SMatthew Dillon 			}
116907485271SMichael Neumann 			if (ForceYesOpt) {
117007485271SMichael Neumann 				xav[xac++] = "-y";
117107485271SMichael Neumann 			}
1172243ca327SMatthew Dillon 			xav[xac++] = "-2";
1173243ca327SMatthew Dillon 			xav[xac++] = "mirror-write";
1174243ca327SMatthew Dillon 			xav[xac++] = ptr;
1175243ca327SMatthew Dillon 			xav[xac++] = NULL;
1176243ca327SMatthew Dillon 			execv("/usr/bin/ssh", (void *)xav);
117734ebae70SMatthew Dillon 		} else {
117834ebae70SMatthew Dillon 			hammer_cmd_mirror_write(av + 1, 1);
1179243ca327SMatthew Dillon 			fflush(stdout);
1180243ca327SMatthew Dillon 			fflush(stderr);
118134ebae70SMatthew Dillon 		}
118253d93cc7SMatthew Dillon 		_exit(1);
118334ebae70SMatthew Dillon 	}
118434ebae70SMatthew Dillon 	close(fds[0]);
118534ebae70SMatthew Dillon 	close(fds[1]);
118634ebae70SMatthew Dillon 
118734ebae70SMatthew Dillon 	while (waitpid(pid1, NULL, 0) <= 0)
118834ebae70SMatthew Dillon 		;
118934ebae70SMatthew Dillon 	while (waitpid(pid2, NULL, 0) <= 0)
119034ebae70SMatthew Dillon 		;
1191e7f926a5SMatthew Dillon 
1192e7f926a5SMatthew Dillon 	/*
1193e7f926a5SMatthew Dillon 	 * If the link is lost restart
1194e7f926a5SMatthew Dillon 	 */
1195e7f926a5SMatthew Dillon 	if (streaming) {
1196e7f926a5SMatthew Dillon 		if (VerboseOpt) {
1197e7f926a5SMatthew Dillon 			fprintf(stderr, "\nLost Link\n");
1198e7f926a5SMatthew Dillon 			fflush(stderr);
1199e7f926a5SMatthew Dillon 		}
12000bd7a37cSMatthew Dillon 		sleep(15 + DelayOpt);
1201e7f926a5SMatthew Dillon 		goto again;
1202e7f926a5SMatthew Dillon 	}
1203e7f926a5SMatthew Dillon 
1204a7fbbf91SMatthew Dillon }
1205a7fbbf91SMatthew Dillon 
1206243ca327SMatthew Dillon /*
1207243ca327SMatthew Dillon  * Read and return multiple mrecords
1208243ca327SMatthew Dillon  */
1209a7fbbf91SMatthew Dillon static int
121017dd83bcSMatthew Dillon read_mrecords(int fd, char *buf, u_int size, hammer_ioc_mrecord_head_t pickup)
1211a7fbbf91SMatthew Dillon {
121217dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
1213a7fbbf91SMatthew Dillon 	u_int count;
1214a7fbbf91SMatthew Dillon 	size_t n;
1215a7fbbf91SMatthew Dillon 	size_t i;
121617dd83bcSMatthew Dillon 	size_t bytes;
121785a8e8a7SMatthew Dillon 	int type;
1218a7fbbf91SMatthew Dillon 
1219a7fbbf91SMatthew Dillon 	count = 0;
1220a7fbbf91SMatthew Dillon 	while (size - count >= HAMMER_MREC_HEADSIZE) {
1221a7fbbf91SMatthew Dillon 		/*
1222a7fbbf91SMatthew Dillon 		 * Cached the record header in case we run out of buffer
1223a7fbbf91SMatthew Dillon 		 * space.
1224a7fbbf91SMatthew Dillon 		 */
122517dd83bcSMatthew Dillon 		fflush(stdout);
1226a7fbbf91SMatthew Dillon 		if (pickup->signature == 0) {
1227a7fbbf91SMatthew Dillon 			for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) {
1228a7fbbf91SMatthew Dillon 				i = read(fd, (char *)pickup + n,
1229a7fbbf91SMatthew Dillon 					 HAMMER_MREC_HEADSIZE - n);
1230a7fbbf91SMatthew Dillon 				if (i <= 0)
1231a7fbbf91SMatthew Dillon 					break;
1232a7fbbf91SMatthew Dillon 			}
1233a7fbbf91SMatthew Dillon 			if (n == 0)
1234a7fbbf91SMatthew Dillon 				break;
1235a7fbbf91SMatthew Dillon 			if (n != HAMMER_MREC_HEADSIZE) {
1236a7fbbf91SMatthew Dillon 				fprintf(stderr, "read_mrecords: short read on pipe\n");
1237a7fbbf91SMatthew Dillon 				exit(1);
1238a7fbbf91SMatthew Dillon 			}
1239a7fbbf91SMatthew Dillon 			if (pickup->signature != HAMMER_IOC_MIRROR_SIGNATURE) {
124034bb69d8SThomas Nikolajsen 				fprintf(stderr, "read_mrecords: malformed record on pipe, "
124134bb69d8SThomas Nikolajsen 					"bad signature\n");
1242a7fbbf91SMatthew Dillon 				exit(1);
1243a7fbbf91SMatthew Dillon 			}
1244a7fbbf91SMatthew Dillon 		}
1245a7fbbf91SMatthew Dillon 		if (pickup->rec_size < HAMMER_MREC_HEADSIZE ||
124617dd83bcSMatthew Dillon 		    pickup->rec_size > sizeof(*mrec) + HAMMER_XBUFSIZE) {
124734bb69d8SThomas Nikolajsen 			fprintf(stderr, "read_mrecords: malformed record on pipe, "
124834bb69d8SThomas Nikolajsen 				"illegal rec_size\n");
1249a7fbbf91SMatthew Dillon 			exit(1);
1250a7fbbf91SMatthew Dillon 		}
1251a7fbbf91SMatthew Dillon 
1252a7fbbf91SMatthew Dillon 		/*
1253a7fbbf91SMatthew Dillon 		 * Stop if we have insufficient space for the record and data.
1254a7fbbf91SMatthew Dillon 		 */
125517dd83bcSMatthew Dillon 		bytes = HAMMER_HEAD_DOALIGN(pickup->rec_size);
125617dd83bcSMatthew Dillon 		if (size - count < bytes)
1257a7fbbf91SMatthew Dillon 			break;
1258a7fbbf91SMatthew Dillon 
1259a7fbbf91SMatthew Dillon 		/*
126085a8e8a7SMatthew Dillon 		 * Stop if the record type is not a REC, SKIP, or PASS,
126185a8e8a7SMatthew Dillon 		 * which are the only types the ioctl supports.  Other types
126285a8e8a7SMatthew Dillon 		 * are used only by the userland protocol.
126385a8e8a7SMatthew Dillon 		 *
126485a8e8a7SMatthew Dillon 		 * Ignore all flags.
1265243ca327SMatthew Dillon 		 */
126685a8e8a7SMatthew Dillon 		type = pickup->type & HAMMER_MRECF_TYPE_LOMASK;
126785a8e8a7SMatthew Dillon 		if (type != HAMMER_MREC_TYPE_PFSD &&
126885a8e8a7SMatthew Dillon 		    type != HAMMER_MREC_TYPE_REC &&
126985a8e8a7SMatthew Dillon 		    type != HAMMER_MREC_TYPE_SKIP &&
127085a8e8a7SMatthew Dillon 		    type != HAMMER_MREC_TYPE_PASS) {
1271243ca327SMatthew Dillon 			break;
127217dd83bcSMatthew Dillon 		}
1273243ca327SMatthew Dillon 
1274243ca327SMatthew Dillon 		/*
1275a7fbbf91SMatthew Dillon 		 * Read the remainder and clear the pickup signature.
1276a7fbbf91SMatthew Dillon 		 */
127717dd83bcSMatthew Dillon 		for (n = HAMMER_MREC_HEADSIZE; n < bytes; n += i) {
127817dd83bcSMatthew Dillon 			i = read(fd, buf + count + n, bytes - n);
1279a7fbbf91SMatthew Dillon 			if (i <= 0)
1280a7fbbf91SMatthew Dillon 				break;
1281a7fbbf91SMatthew Dillon 		}
128217dd83bcSMatthew Dillon 		if (n != bytes) {
1283a7fbbf91SMatthew Dillon 			fprintf(stderr, "read_mrecords: short read on pipe\n");
1284a7fbbf91SMatthew Dillon 			exit(1);
1285a7fbbf91SMatthew Dillon 		}
128617dd83bcSMatthew Dillon 
128717dd83bcSMatthew Dillon 		bcopy(pickup, buf + count, HAMMER_MREC_HEADSIZE);
128817dd83bcSMatthew Dillon 		pickup->signature = 0;
128917dd83bcSMatthew Dillon 		pickup->type = 0;
129017dd83bcSMatthew Dillon 		mrec = (void *)(buf + count);
129117dd83bcSMatthew Dillon 
129217dd83bcSMatthew Dillon 		/*
129317dd83bcSMatthew Dillon 		 * Validate the completed record
129417dd83bcSMatthew Dillon 		 */
129517dd83bcSMatthew Dillon 		if (mrec->head.rec_crc !=
129617dd83bcSMatthew Dillon 		    crc32((char *)mrec + HAMMER_MREC_CRCOFF,
129717dd83bcSMatthew Dillon 			  mrec->head.rec_size - HAMMER_MREC_CRCOFF)) {
129817dd83bcSMatthew Dillon 			fprintf(stderr, "read_mrecords: malformed record "
129917dd83bcSMatthew Dillon 					"on pipe, bad crc\n");
130017dd83bcSMatthew Dillon 			exit(1);
1301a7fbbf91SMatthew Dillon 		}
1302a7fbbf91SMatthew Dillon 
130317dd83bcSMatthew Dillon 		/*
130485a8e8a7SMatthew Dillon 		 * If its a B-Tree record validate the data crc.
130585a8e8a7SMatthew Dillon 		 *
130685a8e8a7SMatthew Dillon 		 * NOTE: If the VFS passes us an explicitly errorde mrec
130785a8e8a7SMatthew Dillon 		 *	 we just pass it through.
130817dd83bcSMatthew Dillon 		 */
130985a8e8a7SMatthew Dillon 		type = mrec->head.type & HAMMER_MRECF_TYPE_MASK;
131085a8e8a7SMatthew Dillon 
131185a8e8a7SMatthew Dillon 		if (type == HAMMER_MREC_TYPE_REC) {
131217dd83bcSMatthew Dillon 			if (mrec->head.rec_size <
131317dd83bcSMatthew Dillon 			    sizeof(mrec->rec) + mrec->rec.leaf.data_len) {
131417dd83bcSMatthew Dillon 				fprintf(stderr,
131517dd83bcSMatthew Dillon 					"read_mrecords: malformed record on "
131617dd83bcSMatthew Dillon 					"pipe, illegal element data_len\n");
131717dd83bcSMatthew Dillon 				exit(1);
131817dd83bcSMatthew Dillon 			}
131917dd83bcSMatthew Dillon 			if (mrec->rec.leaf.data_len &&
132017dd83bcSMatthew Dillon 			    mrec->rec.leaf.data_offset &&
132117dd83bcSMatthew Dillon 			    hammer_crc_test_leaf(&mrec->rec + 1, &mrec->rec.leaf) == 0) {
132217dd83bcSMatthew Dillon 				fprintf(stderr,
132317dd83bcSMatthew Dillon 					"read_mrecords: data_crc did not "
1324a276dc6bSMatthew Dillon 					"match data! obj=%016jx key=%016jx\n",
1325a276dc6bSMatthew Dillon 					(uintmax_t)mrec->rec.leaf.base.obj_id,
1326a276dc6bSMatthew Dillon 					(uintmax_t)mrec->rec.leaf.base.key);
132717dd83bcSMatthew Dillon 				fprintf(stderr,
132817dd83bcSMatthew Dillon 					"continuing, but there are problems\n");
132917dd83bcSMatthew Dillon 			}
133017dd83bcSMatthew Dillon 		}
133117dd83bcSMatthew Dillon 		count += bytes;
1332a7fbbf91SMatthew Dillon 	}
1333a7fbbf91SMatthew Dillon 	return(count);
1334a7fbbf91SMatthew Dillon }
1335a7fbbf91SMatthew Dillon 
133634ebae70SMatthew Dillon /*
133717dd83bcSMatthew Dillon  * Read and return a single mrecord.
1338243ca327SMatthew Dillon  */
1339243ca327SMatthew Dillon static
134017dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t
134117dd83bcSMatthew Dillon read_mrecord(int fdin, int *errorp, hammer_ioc_mrecord_head_t pickup)
1342243ca327SMatthew Dillon {
134317dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
134417dd83bcSMatthew Dillon 	struct hammer_ioc_mrecord_head mrechd;
1345243ca327SMatthew Dillon 	size_t bytes;
1346243ca327SMatthew Dillon 	size_t n;
1347243ca327SMatthew Dillon 	size_t i;
1348243ca327SMatthew Dillon 
1349243ca327SMatthew Dillon 	if (pickup && pickup->type != 0) {
1350243ca327SMatthew Dillon 		mrechd = *pickup;
1351243ca327SMatthew Dillon 		pickup->signature = 0;
1352243ca327SMatthew Dillon 		pickup->type = 0;
1353243ca327SMatthew Dillon 		n = HAMMER_MREC_HEADSIZE;
1354243ca327SMatthew Dillon 	} else {
1355243ca327SMatthew Dillon 		/*
1356243ca327SMatthew Dillon 		 * Read in the PFSD header from the sender.
1357243ca327SMatthew Dillon 		 */
1358243ca327SMatthew Dillon 		for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) {
1359243ca327SMatthew Dillon 			i = read(fdin, (char *)&mrechd + n, HAMMER_MREC_HEADSIZE - n);
1360243ca327SMatthew Dillon 			if (i <= 0)
1361243ca327SMatthew Dillon 				break;
1362243ca327SMatthew Dillon 		}
1363243ca327SMatthew Dillon 		if (n == 0) {
1364243ca327SMatthew Dillon 			*errorp = 0;	/* EOF */
1365243ca327SMatthew Dillon 			return(NULL);
1366243ca327SMatthew Dillon 		}
1367243ca327SMatthew Dillon 		if (n != HAMMER_MREC_HEADSIZE) {
1368243ca327SMatthew Dillon 			fprintf(stderr, "short read of mrecord header\n");
1369243ca327SMatthew Dillon 			*errorp = EPIPE;
1370243ca327SMatthew Dillon 			return(NULL);
1371243ca327SMatthew Dillon 		}
1372243ca327SMatthew Dillon 	}
1373243ca327SMatthew Dillon 	if (mrechd.signature != HAMMER_IOC_MIRROR_SIGNATURE) {
1374243ca327SMatthew Dillon 		fprintf(stderr, "read_mrecord: bad signature\n");
1375243ca327SMatthew Dillon 		*errorp = EINVAL;
1376243ca327SMatthew Dillon 		return(NULL);
1377243ca327SMatthew Dillon 	}
137817dd83bcSMatthew Dillon 	bytes = HAMMER_HEAD_DOALIGN(mrechd.rec_size);
137917dd83bcSMatthew Dillon 	assert(bytes >= sizeof(mrechd));
1380243ca327SMatthew Dillon 	mrec = malloc(bytes);
138117dd83bcSMatthew Dillon 	mrec->head = mrechd;
138217dd83bcSMatthew Dillon 
1383243ca327SMatthew Dillon 	while (n < bytes) {
1384243ca327SMatthew Dillon 		i = read(fdin, (char *)mrec + n, bytes - n);
1385243ca327SMatthew Dillon 		if (i <= 0)
1386243ca327SMatthew Dillon 			break;
1387243ca327SMatthew Dillon 		n += i;
1388243ca327SMatthew Dillon 	}
1389243ca327SMatthew Dillon 	if (n != bytes) {
1390243ca327SMatthew Dillon 		fprintf(stderr, "read_mrecord: short read on payload\n");
1391243ca327SMatthew Dillon 		*errorp = EPIPE;
1392243ca327SMatthew Dillon 		return(NULL);
1393243ca327SMatthew Dillon 	}
139417dd83bcSMatthew Dillon 	if (mrec->head.rec_crc !=
139517dd83bcSMatthew Dillon 	    crc32((char *)mrec + HAMMER_MREC_CRCOFF,
139617dd83bcSMatthew Dillon 		  mrec->head.rec_size - HAMMER_MREC_CRCOFF)) {
1397243ca327SMatthew Dillon 		fprintf(stderr, "read_mrecord: bad CRC\n");
1398243ca327SMatthew Dillon 		*errorp = EINVAL;
1399243ca327SMatthew Dillon 		return(NULL);
1400243ca327SMatthew Dillon 	}
1401243ca327SMatthew Dillon 	*errorp = 0;
1402243ca327SMatthew Dillon 	return(mrec);
1403243ca327SMatthew Dillon }
1404243ca327SMatthew Dillon 
1405243ca327SMatthew Dillon static
1406243ca327SMatthew Dillon void
140717dd83bcSMatthew Dillon write_mrecord(int fdout, u_int32_t type, hammer_ioc_mrecord_any_t mrec,
140817dd83bcSMatthew Dillon 	      int bytes)
1409243ca327SMatthew Dillon {
141017dd83bcSMatthew Dillon 	char zbuf[HAMMER_HEAD_ALIGN];
141117dd83bcSMatthew Dillon 	int pad;
1412243ca327SMatthew Dillon 
141317dd83bcSMatthew Dillon 	pad = HAMMER_HEAD_DOALIGN(bytes) - bytes;
141417dd83bcSMatthew Dillon 
141517dd83bcSMatthew Dillon 	assert(bytes >= (int)sizeof(mrec->head));
141617dd83bcSMatthew Dillon 	bzero(&mrec->head, sizeof(mrec->head));
141717dd83bcSMatthew Dillon 	mrec->head.signature = HAMMER_IOC_MIRROR_SIGNATURE;
141817dd83bcSMatthew Dillon 	mrec->head.type = type;
141917dd83bcSMatthew Dillon 	mrec->head.rec_size = bytes;
142017dd83bcSMatthew Dillon 	mrec->head.rec_crc = crc32((char *)mrec + HAMMER_MREC_CRCOFF,
142117dd83bcSMatthew Dillon 				   bytes - HAMMER_MREC_CRCOFF);
142217dd83bcSMatthew Dillon 	if (write(fdout, mrec, bytes) != bytes) {
1423243ca327SMatthew Dillon 		fprintf(stderr, "write_mrecord: error %d (%s)\n",
1424243ca327SMatthew Dillon 			errno, strerror(errno));
1425243ca327SMatthew Dillon 		exit(1);
1426243ca327SMatthew Dillon 	}
142717dd83bcSMatthew Dillon 	if (pad) {
142817dd83bcSMatthew Dillon 		bzero(zbuf, pad);
142917dd83bcSMatthew Dillon 		if (write(fdout, zbuf, pad) != pad) {
143017dd83bcSMatthew Dillon 			fprintf(stderr, "write_mrecord: error %d (%s)\n",
143117dd83bcSMatthew Dillon 				errno, strerror(errno));
143217dd83bcSMatthew Dillon 			exit(1);
143317dd83bcSMatthew Dillon 		}
143417dd83bcSMatthew Dillon 	}
1435243ca327SMatthew Dillon }
1436243ca327SMatthew Dillon 
1437243ca327SMatthew Dillon /*
143834ebae70SMatthew Dillon  * Generate a mirroring header with the pfs information of the
143934ebae70SMatthew Dillon  * originating filesytem.
144034ebae70SMatthew Dillon  */
144134ebae70SMatthew Dillon static void
1442e7f926a5SMatthew Dillon generate_mrec_header(int fd, int pfs_id,
1443e7f926a5SMatthew Dillon 		     union hammer_ioc_mrecord_any *mrec_tmp)
144434ebae70SMatthew Dillon {
144534ebae70SMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
144634ebae70SMatthew Dillon 
144734ebae70SMatthew Dillon 	bzero(&pfs, sizeof(pfs));
1448e7f926a5SMatthew Dillon 	bzero(mrec_tmp, sizeof(*mrec_tmp));
1449d4e5b69bSMatthew Dillon 	pfs.pfs_id = pfs_id;
1450e7f926a5SMatthew Dillon 	pfs.ondisk = &mrec_tmp->pfs.pfsd;
1451e7f926a5SMatthew Dillon 	pfs.bytes = sizeof(mrec_tmp->pfs.pfsd);
145234ebae70SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
14539dc76cb1SMatthew Dillon 		fprintf(stderr, "Mirror-read: not a HAMMER fs/pseudofs!\n");
145434ebae70SMatthew Dillon 		exit(1);
145534ebae70SMatthew Dillon 	}
145634ebae70SMatthew Dillon 	if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
14579dc76cb1SMatthew Dillon 		fprintf(stderr, "Mirror-read: HAMMER pfs version mismatch!\n");
145834ebae70SMatthew Dillon 		exit(1);
145934ebae70SMatthew Dillon 	}
1460e7f926a5SMatthew Dillon 	mrec_tmp->pfs.version = pfs.version;
146134ebae70SMatthew Dillon }
146234ebae70SMatthew Dillon 
146334ebae70SMatthew Dillon /*
146434ebae70SMatthew Dillon  * Validate the pfs information from the originating filesystem
146534ebae70SMatthew Dillon  * against the target filesystem.  shared_uuid must match.
146648eadef9SMatthew Dillon  *
146748eadef9SMatthew Dillon  * return -1 if we got a TERM record
146834ebae70SMatthew Dillon  */
146948eadef9SMatthew Dillon static int
1470d4e5b69bSMatthew Dillon validate_mrec_header(int fd, int fdin, int is_target, int pfs_id,
147148eadef9SMatthew Dillon 		     struct hammer_ioc_mrecord_head *pickup,
147234ebae70SMatthew Dillon 		     hammer_tid_t *tid_begp, hammer_tid_t *tid_endp)
147334ebae70SMatthew Dillon {
147434ebae70SMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
147534ebae70SMatthew Dillon 	struct hammer_pseudofs_data pfsd;
147617dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
1477243ca327SMatthew Dillon 	int error;
147834ebae70SMatthew Dillon 
147934ebae70SMatthew Dillon 	/*
148034ebae70SMatthew Dillon 	 * Get the PFSD info from the target filesystem.
148134ebae70SMatthew Dillon 	 */
148234ebae70SMatthew Dillon 	bzero(&pfs, sizeof(pfs));
148334ebae70SMatthew Dillon 	bzero(&pfsd, sizeof(pfsd));
1484d4e5b69bSMatthew Dillon 	pfs.pfs_id = pfs_id;
148534ebae70SMatthew Dillon 	pfs.ondisk = &pfsd;
148634ebae70SMatthew Dillon 	pfs.bytes = sizeof(pfsd);
148734ebae70SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
148834ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: not a HAMMER fs/pseudofs!\n");
148934ebae70SMatthew Dillon 		exit(1);
149034ebae70SMatthew Dillon 	}
149134ebae70SMatthew Dillon 	if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
149234ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: HAMMER pfs version mismatch!\n");
149334ebae70SMatthew Dillon 		exit(1);
149434ebae70SMatthew Dillon 	}
149534ebae70SMatthew Dillon 
149648eadef9SMatthew Dillon 	mrec = read_mrecord(fdin, &error, pickup);
1497243ca327SMatthew Dillon 	if (mrec == NULL) {
1498243ca327SMatthew Dillon 		if (error == 0)
1499243ca327SMatthew Dillon 			fprintf(stderr, "validate_mrec_header: short read\n");
150034ebae70SMatthew Dillon 		exit(1);
150134ebae70SMatthew Dillon 	}
150248eadef9SMatthew Dillon 	if (mrec->head.type == HAMMER_MREC_TYPE_TERM) {
150348eadef9SMatthew Dillon 		free(mrec);
150448eadef9SMatthew Dillon 		return(-1);
150548eadef9SMatthew Dillon 	}
150648eadef9SMatthew Dillon 
150717dd83bcSMatthew Dillon 	if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) {
1508243ca327SMatthew Dillon 		fprintf(stderr, "validate_mrec_header: did not get expected "
1509243ca327SMatthew Dillon 				"PFSD record type\n");
151034ebae70SMatthew Dillon 		exit(1);
151134ebae70SMatthew Dillon 	}
151217dd83bcSMatthew Dillon 	if (mrec->head.rec_size != sizeof(mrec->pfs)) {
1513243ca327SMatthew Dillon 		fprintf(stderr, "validate_mrec_header: unexpected payload "
1514243ca327SMatthew Dillon 				"size\n");
151534ebae70SMatthew Dillon 		exit(1);
151634ebae70SMatthew Dillon 	}
151717dd83bcSMatthew Dillon 	if (mrec->pfs.version != pfs.version) {
1518243ca327SMatthew Dillon 		fprintf(stderr, "validate_mrec_header: Version mismatch\n");
151934ebae70SMatthew Dillon 		exit(1);
152034ebae70SMatthew Dillon 	}
152134ebae70SMatthew Dillon 
152234ebae70SMatthew Dillon 	/*
152334ebae70SMatthew Dillon 	 * Whew.  Ok, is the read PFS info compatible with the target?
152434ebae70SMatthew Dillon 	 */
152517dd83bcSMatthew Dillon 	if (bcmp(&mrec->pfs.pfsd.shared_uuid, &pfsd.shared_uuid,
152617dd83bcSMatthew Dillon 		 sizeof(pfsd.shared_uuid)) != 0) {
152717dd83bcSMatthew Dillon 		fprintf(stderr,
152817dd83bcSMatthew Dillon 			"mirror-write: source and target have "
1529f265b84fSSascha Wildner 			"different shared-uuid's!\n");
153034ebae70SMatthew Dillon 		exit(1);
153134ebae70SMatthew Dillon 	}
1532d4e5b69bSMatthew Dillon 	if (is_target &&
1533d4e5b69bSMatthew Dillon 	    (pfsd.mirror_flags & HAMMER_PFSD_SLAVE) == 0) {
153434ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: target must be in slave mode\n");
153534ebae70SMatthew Dillon 		exit(1);
153634ebae70SMatthew Dillon 	}
1537d4e5b69bSMatthew Dillon 	if (tid_begp)
153817dd83bcSMatthew Dillon 		*tid_begp = mrec->pfs.pfsd.sync_beg_tid;
1539d4e5b69bSMatthew Dillon 	if (tid_endp)
154017dd83bcSMatthew Dillon 		*tid_endp = mrec->pfs.pfsd.sync_end_tid;
1541243ca327SMatthew Dillon 	free(mrec);
154248eadef9SMatthew Dillon 	return(0);
154334ebae70SMatthew Dillon }
154434ebae70SMatthew Dillon 
154534ebae70SMatthew Dillon static void
1546d4e5b69bSMatthew Dillon update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id)
154734ebae70SMatthew Dillon {
1548243ca327SMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
1549243ca327SMatthew Dillon 	struct hammer_pseudofs_data pfsd;
155034ebae70SMatthew Dillon 
1551243ca327SMatthew Dillon 	bzero(&pfs, sizeof(pfs));
1552243ca327SMatthew Dillon 	bzero(&pfsd, sizeof(pfsd));
1553d4e5b69bSMatthew Dillon 	pfs.pfs_id = pfs_id;
1554243ca327SMatthew Dillon 	pfs.ondisk = &pfsd;
1555243ca327SMatthew Dillon 	pfs.bytes = sizeof(pfsd);
1556243ca327SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
1557243ca327SMatthew Dillon 		perror("update_pfs_snapshot (read)");
1558243ca327SMatthew Dillon 		exit(1);
155934ebae70SMatthew Dillon 	}
15609c67b4d2SMatthew Dillon 	if (pfsd.sync_end_tid != snapshot_tid) {
1561ddc8e722SMatthew Dillon 		pfsd.sync_end_tid = snapshot_tid;
1562243ca327SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) != 0) {
1563243ca327SMatthew Dillon 			perror("update_pfs_snapshot (rewrite)");
1564243ca327SMatthew Dillon 			exit(1);
156534ebae70SMatthew Dillon 		}
156648eadef9SMatthew Dillon 		if (VerboseOpt >= 2) {
15679dc76cb1SMatthew Dillon 			fprintf(stderr,
1568ced4470dSMatthew Dillon 				"Mirror-write: Completed, updated snapshot "
1569a276dc6bSMatthew Dillon 				"to %016jx\n",
1570a276dc6bSMatthew Dillon 				(uintmax_t)snapshot_tid);
1571ced4470dSMatthew Dillon 			fflush(stderr);
1572243ca327SMatthew Dillon 		}
15739c67b4d2SMatthew Dillon 	}
157448eadef9SMatthew Dillon }
1575243ca327SMatthew Dillon 
157648eadef9SMatthew Dillon /*
157748eadef9SMatthew Dillon  * Bandwidth-limited write in chunks
157848eadef9SMatthew Dillon  */
157948eadef9SMatthew Dillon static
158048eadef9SMatthew Dillon ssize_t
158148eadef9SMatthew Dillon writebw(int fd, const void *buf, size_t nbytes,
158248eadef9SMatthew Dillon 	u_int64_t *bwcount, struct timeval *tv1)
158348eadef9SMatthew Dillon {
158448eadef9SMatthew Dillon 	struct timeval tv2;
158548eadef9SMatthew Dillon 	size_t n;
158648eadef9SMatthew Dillon 	ssize_t r;
158748eadef9SMatthew Dillon 	ssize_t a;
158848eadef9SMatthew Dillon 	int usec;
158948eadef9SMatthew Dillon 
159048eadef9SMatthew Dillon 	a = 0;
159148eadef9SMatthew Dillon 	r = 0;
159248eadef9SMatthew Dillon 	while (nbytes) {
159348eadef9SMatthew Dillon 		if (*bwcount + nbytes > BandwidthOpt)
159448eadef9SMatthew Dillon 			n = BandwidthOpt - *bwcount;
159548eadef9SMatthew Dillon 		else
159648eadef9SMatthew Dillon 			n = nbytes;
159748eadef9SMatthew Dillon 		if (n)
159848eadef9SMatthew Dillon 			r = write(fd, buf, n);
159948eadef9SMatthew Dillon 		if (r >= 0) {
160048eadef9SMatthew Dillon 			a += r;
160148eadef9SMatthew Dillon 			nbytes -= r;
160248eadef9SMatthew Dillon 			buf = (const char *)buf + r;
160348eadef9SMatthew Dillon 		}
160448eadef9SMatthew Dillon 		if ((size_t)r != n)
160548eadef9SMatthew Dillon 			break;
160648eadef9SMatthew Dillon 		*bwcount += n;
160748eadef9SMatthew Dillon 		if (*bwcount >= BandwidthOpt) {
160848eadef9SMatthew Dillon 			gettimeofday(&tv2, NULL);
160948eadef9SMatthew Dillon 			usec = (int)(tv2.tv_sec - tv1->tv_sec) * 1000000 +
161048eadef9SMatthew Dillon 				(int)(tv2.tv_usec - tv1->tv_usec);
161148eadef9SMatthew Dillon 			if (usec >= 0 && usec < 1000000)
161248eadef9SMatthew Dillon 				usleep(1000000 - usec);
161348eadef9SMatthew Dillon 			gettimeofday(tv1, NULL);
161448eadef9SMatthew Dillon 			*bwcount -= BandwidthOpt;
161548eadef9SMatthew Dillon 		}
161648eadef9SMatthew Dillon 	}
161748eadef9SMatthew Dillon 	return(a ? a : r);
161848eadef9SMatthew Dillon }
161934ebae70SMatthew Dillon 
162001a72c9fSMatthew Dillon /*
162101a72c9fSMatthew Dillon  * Get a yes or no answer from the terminal.  The program may be run as
162201a72c9fSMatthew Dillon  * part of a two-way pipe so we cannot use stdin for this operation.
162301a72c9fSMatthew Dillon  */
162401a72c9fSMatthew Dillon static int
162501a72c9fSMatthew Dillon getyn(void)
162601a72c9fSMatthew Dillon {
162701a72c9fSMatthew Dillon 	char buf[256];
162801a72c9fSMatthew Dillon 	FILE *fp;
162901a72c9fSMatthew Dillon 	int result;
163001a72c9fSMatthew Dillon 
163101a72c9fSMatthew Dillon 	fp = fopen("/dev/tty", "r");
163201a72c9fSMatthew Dillon 	if (fp == NULL) {
163301a72c9fSMatthew Dillon 		fprintf(stderr, "No terminal for response\n");
163401a72c9fSMatthew Dillon 		return(-1);
163501a72c9fSMatthew Dillon 	}
163601a72c9fSMatthew Dillon 	result = -1;
163701a72c9fSMatthew Dillon 	while (fgets(buf, sizeof(buf), fp) != NULL) {
163801a72c9fSMatthew Dillon 		if (buf[0] == 'y' || buf[0] == 'Y') {
163901a72c9fSMatthew Dillon 			result = 1;
164001a72c9fSMatthew Dillon 			break;
164101a72c9fSMatthew Dillon 		}
164201a72c9fSMatthew Dillon 		if (buf[0] == 'n' || buf[0] == 'N') {
164301a72c9fSMatthew Dillon 			result = 0;
164401a72c9fSMatthew Dillon 			break;
164501a72c9fSMatthew Dillon 		}
164601a72c9fSMatthew Dillon 		fprintf(stderr, "Response not understood\n");
164701a72c9fSMatthew Dillon 		break;
164801a72c9fSMatthew Dillon 	}
164901a72c9fSMatthew Dillon 	fclose(fp);
165001a72c9fSMatthew Dillon 	return(result);
165101a72c9fSMatthew Dillon }
165201a72c9fSMatthew Dillon 
1653a7fbbf91SMatthew Dillon static void
1654a7fbbf91SMatthew Dillon mirror_usage(int code)
1655a7fbbf91SMatthew Dillon {
1656a7fbbf91SMatthew Dillon 	fprintf(stderr,
165734bb69d8SThomas Nikolajsen 		"hammer mirror-read <filesystem> [begin-tid]\n"
165834bb69d8SThomas Nikolajsen 		"hammer mirror-read-stream <filesystem> [begin-tid]\n"
1659a7fbbf91SMatthew Dillon 		"hammer mirror-write <filesystem>\n"
1660243ca327SMatthew Dillon 		"hammer mirror-dump\n"
166134bb69d8SThomas Nikolajsen 		"hammer mirror-copy [[user@]host:]<filesystem>"
166234bb69d8SThomas Nikolajsen 				  " [[user@]host:]<filesystem>\n"
166334bb69d8SThomas Nikolajsen 		"hammer mirror-stream [[user@]host:]<filesystem>"
166434bb69d8SThomas Nikolajsen 				    " [[user@]host:]<filesystem>\n"
1665a7fbbf91SMatthew Dillon 	);
1666a7fbbf91SMatthew Dillon 	exit(code);
1667a7fbbf91SMatthew Dillon }
166801a72c9fSMatthew Dillon 
1669