xref: /dflybsd-src/sbin/hammer/cmd_mirror.c (revision ced4470d9917b2203783de28b12f8b3b029f93d6)
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  *
3401a72c9fSMatthew Dillon  * $DragonFly: src/sbin/hammer/cmd_mirror.c,v 1.16 2008/11/09 05:22:56 dillon Exp $
35a7fbbf91SMatthew Dillon  */
36a7fbbf91SMatthew Dillon 
37a7fbbf91SMatthew Dillon #include "hammer.h"
38a7fbbf91SMatthew Dillon 
39a7fbbf91SMatthew Dillon #define SERIALBUF_SIZE	(512 * 1024)
40a7fbbf91SMatthew Dillon 
41*ced4470dSMatthew Dillon typedef struct histogram {
42*ced4470dSMatthew Dillon 	hammer_tid_t	tid;
43*ced4470dSMatthew Dillon 	u_int64_t	bytes;
44*ced4470dSMatthew Dillon } *histogram_t;
45*ced4470dSMatthew Dillon 
46a7fbbf91SMatthew Dillon static int read_mrecords(int fd, char *buf, u_int size,
4717dd83bcSMatthew Dillon 			 hammer_ioc_mrecord_head_t pickup);
48e7f926a5SMatthew Dillon static int generate_histogram(int fd, const char *filesystem,
49*ced4470dSMatthew Dillon 			 histogram_t *histogram_ary,
50e7f926a5SMatthew Dillon 			 struct hammer_ioc_mirror_rw *mirror_base);
5117dd83bcSMatthew Dillon static hammer_ioc_mrecord_any_t read_mrecord(int fdin, int *errorp,
5217dd83bcSMatthew Dillon 			 hammer_ioc_mrecord_head_t pickup);
5317dd83bcSMatthew Dillon static void write_mrecord(int fdout, u_int32_t type,
5417dd83bcSMatthew Dillon 			 hammer_ioc_mrecord_any_t mrec, int bytes);
55e7f926a5SMatthew Dillon static void generate_mrec_header(int fd, int pfs_id,
56e7f926a5SMatthew Dillon 			 union hammer_ioc_mrecord_any *mrec_tmp);
5748eadef9SMatthew Dillon static int validate_mrec_header(int fd, int fdin, int is_target, int pfs_id,
5848eadef9SMatthew Dillon 			 struct hammer_ioc_mrecord_head *pickup,
5934ebae70SMatthew Dillon 			 hammer_tid_t *tid_begp, hammer_tid_t *tid_endp);
60d4e5b69bSMatthew Dillon static void update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id);
6148eadef9SMatthew Dillon static ssize_t writebw(int fd, const void *buf, size_t nbytes,
6248eadef9SMatthew Dillon 			u_int64_t *bwcount, struct timeval *tv1);
6301a72c9fSMatthew Dillon static int getyn(void);
64a7fbbf91SMatthew Dillon static void mirror_usage(int code);
65a7fbbf91SMatthew Dillon 
6617dd83bcSMatthew Dillon /*
6717dd83bcSMatthew Dillon  * Generate a mirroring data stream from the specific source over the
6817dd83bcSMatthew Dillon  * entire key range, but restricted to the specified transaction range.
6917dd83bcSMatthew Dillon  *
7017dd83bcSMatthew Dillon  * The HAMMER VFS does most of the work, we add a few new mrecord
7117dd83bcSMatthew Dillon  * types to negotiate the TID ranges and verify that the entire
7217dd83bcSMatthew Dillon  * stream made it to the destination.
7317dd83bcSMatthew Dillon  */
74a7fbbf91SMatthew Dillon void
7548eadef9SMatthew Dillon hammer_cmd_mirror_read(char **av, int ac, int streaming)
76a7fbbf91SMatthew Dillon {
77a7fbbf91SMatthew Dillon 	struct hammer_ioc_mirror_rw mirror;
78d4e5b69bSMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
7917dd83bcSMatthew Dillon 	union hammer_ioc_mrecord_any mrec_tmp;
8048eadef9SMatthew Dillon 	struct hammer_ioc_mrecord_head pickup;
8117dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
82243ca327SMatthew Dillon 	hammer_tid_t sync_tid;
83*ced4470dSMatthew Dillon 	histogram_t histogram_ary;
84a7fbbf91SMatthew Dillon 	const char *filesystem;
85a7fbbf91SMatthew Dillon 	char *buf = malloc(SERIALBUF_SIZE);
86243ca327SMatthew Dillon 	int interrupted = 0;
87243ca327SMatthew Dillon 	int error;
88a7fbbf91SMatthew Dillon 	int fd;
89243ca327SMatthew Dillon 	int n;
9048eadef9SMatthew Dillon 	int didwork;
91e7f926a5SMatthew Dillon 	int histogram;
92*ced4470dSMatthew Dillon 	int histindex;
93*ced4470dSMatthew Dillon 	int histmax;
9448eadef9SMatthew Dillon 	int64_t total_bytes;
95243ca327SMatthew Dillon 	time_t base_t = time(NULL);
9648eadef9SMatthew Dillon 	struct timeval bwtv;
9748eadef9SMatthew Dillon 	u_int64_t bwcount;
98*ced4470dSMatthew Dillon 	u_int64_t estbytes;
99a7fbbf91SMatthew Dillon 
10034bb69d8SThomas Nikolajsen 	if (ac == 0 || ac > 2)
101a7fbbf91SMatthew Dillon 		mirror_usage(1);
102a7fbbf91SMatthew Dillon 	filesystem = av[0];
103a7fbbf91SMatthew Dillon 
10448eadef9SMatthew Dillon 	pickup.signature = 0;
10548eadef9SMatthew Dillon 	pickup.type = 0;
106*ced4470dSMatthew Dillon 	histogram = 0;
107*ced4470dSMatthew Dillon 	histindex = 0;
108*ced4470dSMatthew Dillon 	histmax = 0;
109e7f926a5SMatthew Dillon 	histogram_ary = NULL;
11048eadef9SMatthew Dillon 
11148eadef9SMatthew Dillon again:
112a7fbbf91SMatthew Dillon 	bzero(&mirror, sizeof(mirror));
113a7fbbf91SMatthew Dillon 	hammer_key_beg_init(&mirror.key_beg);
114a7fbbf91SMatthew Dillon 	hammer_key_end_init(&mirror.key_end);
115a7fbbf91SMatthew Dillon 
116d4e5b69bSMatthew Dillon 	fd = getpfs(&pfs, filesystem);
117a7fbbf91SMatthew Dillon 
1183d7b2393SMatthew Dillon 	if (streaming && VerboseOpt && VerboseOpt < 2) {
11948eadef9SMatthew Dillon 		fprintf(stderr, "\nRunning");
12048eadef9SMatthew Dillon 		fflush(stderr);
12148eadef9SMatthew Dillon 	}
12248eadef9SMatthew Dillon 	total_bytes = 0;
12348eadef9SMatthew Dillon 	gettimeofday(&bwtv, NULL);
12448eadef9SMatthew Dillon 	bwcount = 0;
12548eadef9SMatthew Dillon 
126243ca327SMatthew Dillon 	/*
127e7f926a5SMatthew Dillon 	 * Send initial header for the purpose of determining the
128e7f926a5SMatthew Dillon 	 * shared-uuid.
12901a72c9fSMatthew Dillon 	 */
130e7f926a5SMatthew Dillon 	generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp);
131e7f926a5SMatthew Dillon 	write_mrecord(1, HAMMER_MREC_TYPE_PFSD,
132e7f926a5SMatthew Dillon 		      &mrec_tmp, sizeof(mrec_tmp.pfs));
13301a72c9fSMatthew Dillon 
13401a72c9fSMatthew Dillon 	/*
135d4e5b69bSMatthew Dillon 	 * In 2-way mode the target will send us a PFS info packet
136d4e5b69bSMatthew Dillon 	 * first.  Use the target's current snapshot TID as our default
137d4e5b69bSMatthew Dillon 	 * begin TID.
138243ca327SMatthew Dillon 	 */
139d4e5b69bSMatthew Dillon 	mirror.tid_beg = 0;
14048eadef9SMatthew Dillon 	if (TwoWayPipeOpt) {
14148eadef9SMatthew Dillon 		n = validate_mrec_header(fd, 0, 0, pfs.pfs_id, &pickup,
142d4e5b69bSMatthew Dillon 					 NULL, &mirror.tid_beg);
14348eadef9SMatthew Dillon 		if (n < 0) {	/* got TERM record */
14448eadef9SMatthew Dillon 			relpfs(fd, &pfs);
14548eadef9SMatthew Dillon 			return;
14648eadef9SMatthew Dillon 		}
14748eadef9SMatthew Dillon 		++mirror.tid_beg;
14848eadef9SMatthew Dillon 	}
149d4e5b69bSMatthew Dillon 
150d4e5b69bSMatthew Dillon 	/*
151d4e5b69bSMatthew Dillon 	 * Write out the PFS header, tid_beg will be updated if our PFS
152d4e5b69bSMatthew Dillon 	 * has a larger begin sync.  tid_end is set to the latest source
153d4e5b69bSMatthew Dillon 	 * TID whos flush cycle has completed.
154d4e5b69bSMatthew Dillon 	 */
155e7f926a5SMatthew Dillon 	generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp);
156e7f926a5SMatthew Dillon 	if (mirror.tid_beg < mrec_tmp.pfs.pfsd.sync_beg_tid)
157e7f926a5SMatthew Dillon 		mirror.tid_beg = mrec_tmp.pfs.pfsd.sync_beg_tid;
158e7f926a5SMatthew Dillon 	mirror.tid_end = mrec_tmp.pfs.pfsd.sync_end_tid;
159e7f926a5SMatthew Dillon 	mirror.ubuf = buf;
160e7f926a5SMatthew Dillon 	mirror.size = SERIALBUF_SIZE;
161e7f926a5SMatthew Dillon 	mirror.pfs_id = pfs.pfs_id;
162e7f926a5SMatthew Dillon 	mirror.shared_uuid = pfs.ondisk->shared_uuid;
163d4e5b69bSMatthew Dillon 
164d4e5b69bSMatthew Dillon 	/*
165e7f926a5SMatthew Dillon 	 * XXX If the histogram is exhausted and the TID delta is large
166e7f926a5SMatthew Dillon 	 *     the stream might have been offline for a while and is
167e7f926a5SMatthew Dillon 	 *     now picking it up again.  Do another histogram.
168d4e5b69bSMatthew Dillon 	 */
169e7f926a5SMatthew Dillon #if 0
170*ced4470dSMatthew Dillon 	if (TwoWayPipeOpt && streaming && histogram && histindex == histend) {
171e7f926a5SMatthew Dillon 		if (mirror.tid_end - mirror.tid_beg > BULK_MINIMUM)
172*ced4470dSMatthew Dillon 			histogram = 0;
173e7f926a5SMatthew Dillon 	}
174e7f926a5SMatthew Dillon #endif
17534ebae70SMatthew Dillon 
176e7f926a5SMatthew Dillon 	/*
177e7f926a5SMatthew Dillon 	 * Initial bulk startup control, try to do some incremental
178e7f926a5SMatthew Dillon 	 * mirroring in order to allow the stream to be killed and
179e7f926a5SMatthew Dillon 	 * restarted without having to start over.
180e7f926a5SMatthew Dillon 	 */
181*ced4470dSMatthew Dillon 	if (histogram == 0 && BulkOpt == 0) {
182e7f926a5SMatthew Dillon 		if (VerboseOpt)
183e7f926a5SMatthew Dillon 			fprintf(stderr, "\n");
184*ced4470dSMatthew Dillon 		histmax = generate_histogram(fd, filesystem,
185e7f926a5SMatthew Dillon 					     &histogram_ary, &mirror);
186*ced4470dSMatthew Dillon 		histindex = 0;
187*ced4470dSMatthew Dillon 		histogram = 1;
188e7f926a5SMatthew Dillon 	}
189e7f926a5SMatthew Dillon 
190*ced4470dSMatthew Dillon 	if (TwoWayPipeOpt && streaming && histogram) {
191*ced4470dSMatthew Dillon 		++histindex;
192*ced4470dSMatthew Dillon 		mirror.tid_end = histogram_ary[histindex].tid;
193*ced4470dSMatthew Dillon 		estbytes = histogram_ary[histindex-1].bytes;
194e7f926a5SMatthew Dillon 		mrec_tmp.pfs.pfsd.sync_end_tid = mirror.tid_end;
195*ced4470dSMatthew Dillon 	} else {
196*ced4470dSMatthew Dillon 		estbytes = 0;
197e7f926a5SMatthew Dillon 	}
198e7f926a5SMatthew Dillon 
199e7f926a5SMatthew Dillon 	write_mrecord(1, HAMMER_MREC_TYPE_PFSD,
200e7f926a5SMatthew Dillon 		      &mrec_tmp, sizeof(mrec_tmp.pfs));
201e7f926a5SMatthew Dillon 
202e7f926a5SMatthew Dillon 	/*
203e7f926a5SMatthew Dillon 	 * A cycle file overrides the beginning TID only if we are
204e7f926a5SMatthew Dillon 	 * not operating in two-way mode.
205e7f926a5SMatthew Dillon 	 */
206e7f926a5SMatthew Dillon 	if (TwoWayPipeOpt == 0) {
207e7f926a5SMatthew Dillon 		hammer_get_cycle(&mirror.key_beg, &mirror.tid_beg);
208e7f926a5SMatthew Dillon 	}
209e7f926a5SMatthew Dillon 
210e7f926a5SMatthew Dillon 	/*
211e7f926a5SMatthew Dillon 	 * An additional argument overrides the beginning TID regardless
212e7f926a5SMatthew Dillon 	 * of what mode we are in.  This is not recommending if operating
213e7f926a5SMatthew Dillon 	 * in two-way mode.
214e7f926a5SMatthew Dillon 	 */
21517dd83bcSMatthew Dillon 	if (ac == 2)
21617dd83bcSMatthew Dillon 		mirror.tid_beg = strtoull(av[1], NULL, 0);
21717dd83bcSMatthew Dillon 
21848eadef9SMatthew Dillon 	if (streaming == 0 || VerboseOpt >= 2) {
21948eadef9SMatthew Dillon 		fprintf(stderr,
220*ced4470dSMatthew Dillon 			"Mirror-read: Mirror %016jx to %016jx",
221a276dc6bSMatthew Dillon 			(uintmax_t)mirror.tid_beg, (uintmax_t)mirror.tid_end);
222*ced4470dSMatthew Dillon 		if (histogram)
223*ced4470dSMatthew Dillon 			fprintf(stderr, " (bulk= %ju)", (uintmax_t)estbytes);
2243d7b2393SMatthew Dillon 		fprintf(stderr, "\n");
2253d7b2393SMatthew Dillon 		fflush(stderr);
22648eadef9SMatthew Dillon 	}
227243ca327SMatthew Dillon 	if (mirror.key_beg.obj_id != (int64_t)HAMMER_MIN_OBJID) {
228a276dc6bSMatthew Dillon 		fprintf(stderr, "Mirror-read: Resuming at object %016jx\n",
229a276dc6bSMatthew Dillon 			(uintmax_t)mirror.key_beg.obj_id);
230243ca327SMatthew Dillon 	}
231243ca327SMatthew Dillon 
232243ca327SMatthew Dillon 	/*
2339dc76cb1SMatthew Dillon 	 * Nothing to do if begin equals end.
2349dc76cb1SMatthew Dillon 	 */
23548eadef9SMatthew Dillon 	if (mirror.tid_beg >= mirror.tid_end) {
23648eadef9SMatthew Dillon 		if (streaming == 0 || VerboseOpt >= 2)
23748eadef9SMatthew Dillon 			fprintf(stderr, "Mirror-read: No work to do\n");
23848eadef9SMatthew Dillon 		didwork = 0;
2399dc76cb1SMatthew Dillon 		goto done;
2409dc76cb1SMatthew Dillon 	}
24148eadef9SMatthew Dillon 	didwork = 1;
2429dc76cb1SMatthew Dillon 
2439dc76cb1SMatthew Dillon 	/*
244243ca327SMatthew Dillon 	 * Write out bulk records
245243ca327SMatthew Dillon 	 */
246a7fbbf91SMatthew Dillon 	mirror.ubuf = buf;
247a7fbbf91SMatthew Dillon 	mirror.size = SERIALBUF_SIZE;
248a7fbbf91SMatthew Dillon 
249a7fbbf91SMatthew Dillon 	do {
250a7fbbf91SMatthew Dillon 		mirror.count = 0;
251d4e5b69bSMatthew Dillon 		mirror.pfs_id = pfs.pfs_id;
252d4e5b69bSMatthew Dillon 		mirror.shared_uuid = pfs.ondisk->shared_uuid;
253a7fbbf91SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) {
254a7fbbf91SMatthew Dillon 			fprintf(stderr, "Mirror-read %s failed: %s\n",
255a7fbbf91SMatthew Dillon 				filesystem, strerror(errno));
256a7fbbf91SMatthew Dillon 			exit(1);
257a7fbbf91SMatthew Dillon 		}
2589c67b4d2SMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) {
2599c67b4d2SMatthew Dillon 			fprintf(stderr,
2609c67b4d2SMatthew Dillon 				"Mirror-read %s fatal error %d\n",
2619c67b4d2SMatthew Dillon 				filesystem, mirror.head.error);
2629c67b4d2SMatthew Dillon 			exit(1);
2639c67b4d2SMatthew Dillon 		}
264243ca327SMatthew Dillon 		if (mirror.count) {
26548eadef9SMatthew Dillon 			if (BandwidthOpt) {
26648eadef9SMatthew Dillon 				n = writebw(1, mirror.ubuf, mirror.count,
26748eadef9SMatthew Dillon 					    &bwcount, &bwtv);
26848eadef9SMatthew Dillon 			} else {
269243ca327SMatthew Dillon 				n = write(1, mirror.ubuf, mirror.count);
27048eadef9SMatthew Dillon 			}
271243ca327SMatthew Dillon 			if (n != mirror.count) {
272243ca327SMatthew Dillon 				fprintf(stderr, "Mirror-read %s failed: "
273243ca327SMatthew Dillon 						"short write\n",
274243ca327SMatthew Dillon 				filesystem);
275243ca327SMatthew Dillon 				exit(1);
276243ca327SMatthew Dillon 			}
277a7fbbf91SMatthew Dillon 		}
27848eadef9SMatthew Dillon 		total_bytes += mirror.count;
27948eadef9SMatthew Dillon 		if (streaming && VerboseOpt) {
280e7f926a5SMatthew Dillon 			fprintf(stderr,
281a276dc6bSMatthew Dillon 				"\robj=%016jx tids=%016jx:%016jx %11jd",
282a276dc6bSMatthew Dillon 				(uintmax_t)mirror.key_cur.obj_id,
283a276dc6bSMatthew Dillon 				(uintmax_t)mirror.tid_beg,
284a276dc6bSMatthew Dillon 				(uintmax_t)mirror.tid_end,
285a276dc6bSMatthew Dillon 				(intmax_t)total_bytes);
28648eadef9SMatthew Dillon 			fflush(stderr);
28748eadef9SMatthew Dillon 		}
288a7fbbf91SMatthew Dillon 		mirror.key_beg = mirror.key_cur;
289e7f926a5SMatthew Dillon 
290e7f926a5SMatthew Dillon 		/*
291e7f926a5SMatthew Dillon 		 * Deal with time limit option
292e7f926a5SMatthew Dillon 		 */
293243ca327SMatthew Dillon 		if (TimeoutOpt &&
294243ca327SMatthew Dillon 		    (unsigned)(time(NULL) - base_t) > (unsigned)TimeoutOpt) {
295243ca327SMatthew Dillon 			fprintf(stderr,
296243ca327SMatthew Dillon 				"Mirror-read %s interrupted by timer at"
297a276dc6bSMatthew Dillon 				" %016jx\n",
298243ca327SMatthew Dillon 				filesystem,
299a276dc6bSMatthew Dillon 				(uintmax_t)mirror.key_cur.obj_id);
300243ca327SMatthew Dillon 			interrupted = 1;
301243ca327SMatthew Dillon 			break;
302243ca327SMatthew Dillon 		}
303a7fbbf91SMatthew Dillon 	} while (mirror.count != 0);
304a7fbbf91SMatthew Dillon 
30548eadef9SMatthew Dillon done:
306*ced4470dSMatthew Dillon 	if (streaming && VerboseOpt) {
307*ced4470dSMatthew Dillon 		fprintf(stderr, "\n");
308*ced4470dSMatthew Dillon 		fflush(stderr);
309*ced4470dSMatthew Dillon 	}
310*ced4470dSMatthew Dillon 
311243ca327SMatthew Dillon 	/*
31248eadef9SMatthew Dillon 	 * Write out the termination sync record - only if not interrupted
313243ca327SMatthew Dillon 	 */
31448eadef9SMatthew Dillon 	if (interrupted == 0) {
31548eadef9SMatthew Dillon 		if (didwork) {
31617dd83bcSMatthew Dillon 			write_mrecord(1, HAMMER_MREC_TYPE_SYNC,
31717dd83bcSMatthew Dillon 				      &mrec_tmp, sizeof(mrec_tmp.sync));
31848eadef9SMatthew Dillon 		} else {
31948eadef9SMatthew Dillon 			write_mrecord(1, HAMMER_MREC_TYPE_IDLE,
32048eadef9SMatthew Dillon 				      &mrec_tmp, sizeof(mrec_tmp.sync));
32148eadef9SMatthew Dillon 		}
32248eadef9SMatthew Dillon 	}
32334ebae70SMatthew Dillon 
324243ca327SMatthew Dillon 	/*
325243ca327SMatthew Dillon 	 * If the -2 option was given (automatic when doing mirror-copy),
326243ca327SMatthew Dillon 	 * a two-way pipe is assumed and we expect a response mrec from
327243ca327SMatthew Dillon 	 * the target.
328243ca327SMatthew Dillon 	 */
329243ca327SMatthew Dillon 	if (TwoWayPipeOpt) {
33048eadef9SMatthew Dillon 		mrec = read_mrecord(0, &error, &pickup);
33117dd83bcSMatthew Dillon 		if (mrec == NULL ||
33217dd83bcSMatthew Dillon 		    mrec->head.type != HAMMER_MREC_TYPE_UPDATE ||
33317dd83bcSMatthew Dillon 		    mrec->head.rec_size != sizeof(mrec->update)) {
334243ca327SMatthew Dillon 			fprintf(stderr, "mirror_read: Did not get final "
335243ca327SMatthew Dillon 					"acknowledgement packet from target\n");
336243ca327SMatthew Dillon 			exit(1);
337243ca327SMatthew Dillon 		}
338243ca327SMatthew Dillon 		if (interrupted) {
339243ca327SMatthew Dillon 			if (CyclePath) {
340*ced4470dSMatthew Dillon 				hammer_set_cycle(&mirror.key_cur,
341*ced4470dSMatthew Dillon 						 mirror.tid_beg);
34234bb69d8SThomas Nikolajsen 				fprintf(stderr, "Cyclefile %s updated for "
34334bb69d8SThomas Nikolajsen 					"continuation\n", CyclePath);
344243ca327SMatthew Dillon 			}
345243ca327SMatthew Dillon 		} else {
34617dd83bcSMatthew Dillon 			sync_tid = mrec->update.tid;
347243ca327SMatthew Dillon 			if (CyclePath) {
348243ca327SMatthew Dillon 				hammer_key_beg_init(&mirror.key_beg);
349243ca327SMatthew Dillon 				hammer_set_cycle(&mirror.key_beg, sync_tid);
350a276dc6bSMatthew Dillon 				fprintf(stderr,
351a276dc6bSMatthew Dillon 					"Cyclefile %s updated to 0x%016jx\n",
352a276dc6bSMatthew Dillon 					CyclePath, (uintmax_t)sync_tid);
353243ca327SMatthew Dillon 			}
354243ca327SMatthew Dillon 		}
355243ca327SMatthew Dillon 	} else if (CyclePath) {
356243ca327SMatthew Dillon 		/* NOTE! mirror.tid_beg cannot be updated */
357243ca327SMatthew Dillon 		fprintf(stderr, "Warning: cycle file (-c option) cannot be "
358243ca327SMatthew Dillon 				"fully updated unless you use mirror-copy\n");
359243ca327SMatthew Dillon 		hammer_set_cycle(&mirror.key_beg, mirror.tid_beg);
360243ca327SMatthew Dillon 	}
36148eadef9SMatthew Dillon 	if (streaming && interrupted == 0) {
36248eadef9SMatthew Dillon 		time_t t1 = time(NULL);
36348eadef9SMatthew Dillon 		time_t t2;
36448eadef9SMatthew Dillon 
365e7f926a5SMatthew Dillon 		/*
366e7f926a5SMatthew Dillon 		 * Two way streaming tries to break down large bulk
367e7f926a5SMatthew Dillon 		 * transfers into smaller ones so it can sync the
368e7f926a5SMatthew Dillon 		 * transaction id on the slave.  This way if we get
369e7f926a5SMatthew Dillon 		 * interrupted a restart doesn't have to start from
370e7f926a5SMatthew Dillon 		 * scratch.
371e7f926a5SMatthew Dillon 		 */
372*ced4470dSMatthew Dillon 		if (TwoWayPipeOpt && streaming && histogram) {
373*ced4470dSMatthew Dillon 			if (histindex != histmax) {
3743d7b2393SMatthew Dillon 				if (VerboseOpt && VerboseOpt < 2)
375e7f926a5SMatthew Dillon 					fprintf(stderr, " (bulk incremental)");
376e7f926a5SMatthew Dillon 				goto again;
377e7f926a5SMatthew Dillon 			}
378*ced4470dSMatthew Dillon 		}
379e7f926a5SMatthew Dillon 
38048eadef9SMatthew Dillon 		if (VerboseOpt) {
38148eadef9SMatthew Dillon 			fprintf(stderr, " W");
38248eadef9SMatthew Dillon 			fflush(stderr);
38348eadef9SMatthew Dillon 		}
38448eadef9SMatthew Dillon 		pfs.ondisk->sync_end_tid = mirror.tid_end;
38548eadef9SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_WAI_PSEUDOFS, &pfs) < 0) {
38648eadef9SMatthew Dillon 			fprintf(stderr, "Mirror-read %s: cannot stream: %s\n",
38748eadef9SMatthew Dillon 				filesystem, strerror(errno));
38848eadef9SMatthew Dillon 		} else {
38948eadef9SMatthew Dillon 			t2 = time(NULL) - t1;
39048eadef9SMatthew Dillon 			if (t2 >= 0 && t2 < DelayOpt) {
39148eadef9SMatthew Dillon 				if (VerboseOpt) {
39248eadef9SMatthew Dillon 					fprintf(stderr, "\bD");
39348eadef9SMatthew Dillon 					fflush(stderr);
39448eadef9SMatthew Dillon 				}
39548eadef9SMatthew Dillon 				sleep(DelayOpt - t2);
39648eadef9SMatthew Dillon 			}
39748eadef9SMatthew Dillon 			if (VerboseOpt) {
39848eadef9SMatthew Dillon 				fprintf(stderr, "\b ");
39948eadef9SMatthew Dillon 				fflush(stderr);
40048eadef9SMatthew Dillon 			}
40148eadef9SMatthew Dillon 			relpfs(fd, &pfs);
40248eadef9SMatthew Dillon 			goto again;
40348eadef9SMatthew Dillon 		}
40448eadef9SMatthew Dillon 	}
40548eadef9SMatthew Dillon 	write_mrecord(1, HAMMER_MREC_TYPE_TERM,
40648eadef9SMatthew Dillon 		      &mrec_tmp, sizeof(mrec_tmp.sync));
40748eadef9SMatthew Dillon 	relpfs(fd, &pfs);
408a7fbbf91SMatthew Dillon 	fprintf(stderr, "Mirror-read %s succeeded\n", filesystem);
409a7fbbf91SMatthew Dillon }
410a7fbbf91SMatthew Dillon 
411e7f926a5SMatthew Dillon /*
4123d7b2393SMatthew Dillon  * What we are trying to do here is figure out how much data is
4133d7b2393SMatthew Dillon  * going to be sent for the TID range and to break the TID range
4143d7b2393SMatthew Dillon  * down into reasonably-sized slices (from the point of view of
4153d7b2393SMatthew Dillon  * data sent) so a lost connection can restart at a reasonable
4163d7b2393SMatthew Dillon  * place and not all the way back at the beginning.
417*ced4470dSMatthew Dillon  *
418*ced4470dSMatthew Dillon  * An entry's TID serves as the end_tid for the prior entry
419*ced4470dSMatthew Dillon  * So we have to offset the calculation by 1 so that TID falls into
420*ced4470dSMatthew Dillon  * the previous entry when populating entries.
421*ced4470dSMatthew Dillon  *
422*ced4470dSMatthew Dillon  * Because the transaction id space is bursty we need a relatively
423*ced4470dSMatthew Dillon  * large number of buckets (like a million) to do a reasonable job
424*ced4470dSMatthew Dillon  * for things like an initial bulk mirrors on a very large filesystem.
425e7f926a5SMatthew Dillon  */
426*ced4470dSMatthew Dillon #define HIST_COUNT	(1024 * 1024)
4273d7b2393SMatthew Dillon 
428e7f926a5SMatthew Dillon static int
429e7f926a5SMatthew Dillon generate_histogram(int fd, const char *filesystem,
430*ced4470dSMatthew Dillon 		   histogram_t *histogram_ary,
431e7f926a5SMatthew Dillon 		   struct hammer_ioc_mirror_rw *mirror_base)
432e7f926a5SMatthew Dillon {
433e7f926a5SMatthew Dillon 	struct hammer_ioc_mirror_rw mirror;
4343d7b2393SMatthew Dillon 	union hammer_ioc_mrecord_any *mrec;
435e7f926a5SMatthew Dillon 	hammer_tid_t tid_beg;
436e7f926a5SMatthew Dillon 	hammer_tid_t tid_end;
437*ced4470dSMatthew Dillon 	hammer_tid_t tid;
438*ced4470dSMatthew Dillon 	hammer_tid_t tidx;
439*ced4470dSMatthew Dillon 	u_int64_t *tid_bytes;
4403d7b2393SMatthew Dillon 	u_int64_t total;
4413d7b2393SMatthew Dillon 	u_int64_t accum;
442e7f926a5SMatthew Dillon 	int i;
4433d7b2393SMatthew Dillon 	int res;
4443d7b2393SMatthew Dillon 	int off;
4453d7b2393SMatthew Dillon 	int len;
446e7f926a5SMatthew Dillon 
447e7f926a5SMatthew Dillon 	mirror = *mirror_base;
448e7f926a5SMatthew Dillon 	tid_beg = mirror.tid_beg;
449e7f926a5SMatthew Dillon 	tid_end = mirror.tid_end;
4503d7b2393SMatthew Dillon 	mirror.head.flags |= HAMMER_IOC_MIRROR_NODATA;
451e7f926a5SMatthew Dillon 
4523d7b2393SMatthew Dillon 	if (*histogram_ary == NULL) {
453*ced4470dSMatthew Dillon 		*histogram_ary = malloc(sizeof(struct histogram) *
4543d7b2393SMatthew Dillon 					(HIST_COUNT + 2));
4553d7b2393SMatthew Dillon 	}
4563d7b2393SMatthew Dillon 	if (tid_beg >= tid_end)
457e7f926a5SMatthew Dillon 		return(0);
458e7f926a5SMatthew Dillon 
459*ced4470dSMatthew Dillon 	/* needs 2 extra */
460*ced4470dSMatthew Dillon 	tid_bytes = malloc(sizeof(*tid_bytes) * (HIST_COUNT + 2));
461*ced4470dSMatthew Dillon 	bzero(tid_bytes, sizeof(tid_bytes));
462*ced4470dSMatthew Dillon 
4633d7b2393SMatthew Dillon 	fprintf(stderr, "Prescan to break up bulk transfer");
4643d7b2393SMatthew Dillon 	if (VerboseOpt > 1)
4653d7b2393SMatthew Dillon 		fprintf(stderr, " (%juMB chunks)",
4663d7b2393SMatthew Dillon 			(uintmax_t)(SplitupOpt / (1024 * 1024)));
4673d7b2393SMatthew Dillon 	fprintf(stderr, "\n");
468e7f926a5SMatthew Dillon 
469*ced4470dSMatthew Dillon 	/*
470*ced4470dSMatthew Dillon 	 * Note: (tid_beg,tid_end), range is inclusive of both beg & end.
471*ced4470dSMatthew Dillon 	 *
472*ced4470dSMatthew Dillon 	 * Note: Estimates can be off when the mirror is way behind due
473*ced4470dSMatthew Dillon 	 *	 to skips.
474*ced4470dSMatthew Dillon 	 */
4753d7b2393SMatthew Dillon 	total = 0;
4763d7b2393SMatthew Dillon 	accum = 0;
4773d7b2393SMatthew Dillon 	for (;;) {
4783d7b2393SMatthew Dillon 		mirror.count = 0;
479e7f926a5SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) {
480e7f926a5SMatthew Dillon 			fprintf(stderr, "Mirror-read %s failed: %s\n",
481e7f926a5SMatthew Dillon 				filesystem, strerror(errno));
482e7f926a5SMatthew Dillon 			exit(1);
483e7f926a5SMatthew Dillon 		}
484e7f926a5SMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) {
485e7f926a5SMatthew Dillon 			fprintf(stderr,
486e7f926a5SMatthew Dillon 				"Mirror-read %s fatal error %d\n",
487e7f926a5SMatthew Dillon 				filesystem, mirror.head.error);
488e7f926a5SMatthew Dillon 			exit(1);
489e7f926a5SMatthew Dillon 		}
4903d7b2393SMatthew Dillon 		for (off = 0;
4913d7b2393SMatthew Dillon 		     off < mirror.count;
4923d7b2393SMatthew Dillon 		     off += HAMMER_HEAD_DOALIGN(mrec->head.rec_size)
4933d7b2393SMatthew Dillon 		) {
4943d7b2393SMatthew Dillon 			mrec = (void *)((char *)mirror.ubuf + off);
495e7f926a5SMatthew Dillon 
496e7f926a5SMatthew Dillon 			/*
497*ced4470dSMatthew Dillon 			 * We only care about general RECs and PASS
498*ced4470dSMatthew Dillon 			 * records.  We ignore SKIPs.
499e7f926a5SMatthew Dillon 			 */
500*ced4470dSMatthew Dillon 			switch (mrec->head.type & HAMMER_MRECF_TYPE_LOMASK) {
501*ced4470dSMatthew Dillon 			case HAMMER_MREC_TYPE_REC:
502*ced4470dSMatthew Dillon 			case HAMMER_MREC_TYPE_PASS:
503*ced4470dSMatthew Dillon 				break;
504*ced4470dSMatthew Dillon 			default:
505*ced4470dSMatthew Dillon 				continue;
506e7f926a5SMatthew Dillon 			}
5073d7b2393SMatthew Dillon 
508*ced4470dSMatthew Dillon 			/*
509*ced4470dSMatthew Dillon 			 * Calculate for two indices, create_tid and
510*ced4470dSMatthew Dillon 			 * delete_tid.  Record data only applies to
511*ced4470dSMatthew Dillon 			 * the create_tid.
512*ced4470dSMatthew Dillon 			 *
513*ced4470dSMatthew Dillon 			 * When tid is exactly on the boundary it really
514*ced4470dSMatthew Dillon 			 * belongs to the previous entry because scans
515*ced4470dSMatthew Dillon 			 * are inclusive of the ending entry.
516*ced4470dSMatthew Dillon 			 */
517*ced4470dSMatthew Dillon 			tid = mrec->rec.leaf.base.delete_tid;
518*ced4470dSMatthew Dillon 			if (tid && tid >= tid_beg && tid <= tid_end) {
519*ced4470dSMatthew Dillon 				len = HAMMER_HEAD_DOALIGN(mrec->head.rec_size);
520*ced4470dSMatthew Dillon 				if (mrec->head.type ==
521*ced4470dSMatthew Dillon 				    HAMMER_MREC_TYPE_REC) {
522*ced4470dSMatthew Dillon 					len -= HAMMER_HEAD_DOALIGN(
523*ced4470dSMatthew Dillon 						    mrec->rec.leaf.data_len);
524*ced4470dSMatthew Dillon 					assert(len > 0);
525*ced4470dSMatthew Dillon 				}
526*ced4470dSMatthew Dillon 				i = (tid - tid_beg) * HIST_COUNT /
5273d7b2393SMatthew Dillon 				    (tid_end - tid_beg);
528*ced4470dSMatthew Dillon 				tidx = tid_beg + i * (tid_end - tid_beg) /
529*ced4470dSMatthew Dillon 						 HIST_COUNT;
530*ced4470dSMatthew Dillon 				if (tid == tidx && i)
531*ced4470dSMatthew Dillon 					--i;
532*ced4470dSMatthew Dillon 				assert(i >= 0 && i < HIST_COUNT);
5333d7b2393SMatthew Dillon 				tid_bytes[i] += len;
5343d7b2393SMatthew Dillon 				total += len;
5353d7b2393SMatthew Dillon 				accum += len;
5363d7b2393SMatthew Dillon 			}
537*ced4470dSMatthew Dillon 
538*ced4470dSMatthew Dillon 			tid = mrec->rec.leaf.base.create_tid;
539*ced4470dSMatthew Dillon 			if (tid && tid >= tid_beg && tid <= tid_end) {
540*ced4470dSMatthew Dillon 				len = HAMMER_HEAD_DOALIGN(mrec->head.rec_size);
541*ced4470dSMatthew Dillon 				if (mrec->head.type ==
542*ced4470dSMatthew Dillon 				    HAMMER_MREC_TYPE_REC_NODATA) {
543*ced4470dSMatthew Dillon 					len += HAMMER_HEAD_DOALIGN(
544*ced4470dSMatthew Dillon 						    mrec->rec.leaf.data_len);
545*ced4470dSMatthew Dillon 				}
546*ced4470dSMatthew Dillon 				i = (tid - tid_beg) * HIST_COUNT /
547*ced4470dSMatthew Dillon 				    (tid_end - tid_beg);
548*ced4470dSMatthew Dillon 				tidx = tid_beg + i * (tid_end - tid_beg) /
549*ced4470dSMatthew Dillon 						 HIST_COUNT;
550*ced4470dSMatthew Dillon 				if (tid == tidx && i)
551*ced4470dSMatthew Dillon 					--i;
552*ced4470dSMatthew Dillon 				assert(i >= 0 && i < HIST_COUNT);
553*ced4470dSMatthew Dillon 				tid_bytes[i] += len;
554*ced4470dSMatthew Dillon 				total += len;
555*ced4470dSMatthew Dillon 				accum += len;
5563d7b2393SMatthew Dillon 			}
5573d7b2393SMatthew Dillon 		}
5583d7b2393SMatthew Dillon 		if (VerboseOpt > 1) {
5593d7b2393SMatthew Dillon 			if (accum > SplitupOpt) {
5603d7b2393SMatthew Dillon 				fprintf(stderr, ".");
5613d7b2393SMatthew Dillon 				fflush(stderr);
5623d7b2393SMatthew Dillon 				accum = 0;
5633d7b2393SMatthew Dillon 			}
5643d7b2393SMatthew Dillon 		}
5653d7b2393SMatthew Dillon 		if (mirror.count == 0)
5663d7b2393SMatthew Dillon 			break;
5673d7b2393SMatthew Dillon 		mirror.key_beg = mirror.key_cur;
5683d7b2393SMatthew Dillon 	}
5693d7b2393SMatthew Dillon 
5703d7b2393SMatthew Dillon 	/*
5713d7b2393SMatthew Dillon 	 * Reduce to SplitupOpt (default 100MB) chunks.  This code may
572*ced4470dSMatthew Dillon 	 * use up to two additional elements.  Do the array in-place.
573*ced4470dSMatthew Dillon 	 *
574*ced4470dSMatthew Dillon 	 * Inefficient degenerate cases can occur if we do not accumulate
575*ced4470dSMatthew Dillon 	 * at least the requested split amount, so error on the side of
576*ced4470dSMatthew Dillon 	 * going over a bit.
5773d7b2393SMatthew Dillon 	 */
5783d7b2393SMatthew Dillon 	res = 0;
579*ced4470dSMatthew Dillon 	(*histogram_ary)[res].tid = tid_beg;
580*ced4470dSMatthew Dillon 	(*histogram_ary)[res].bytes = tid_bytes[0];
581*ced4470dSMatthew Dillon 	for (i = 1; i < HIST_COUNT; ++i) {
582*ced4470dSMatthew Dillon 		if ((*histogram_ary)[res].bytes >= SplitupOpt) {
583*ced4470dSMatthew Dillon 			++res;
584*ced4470dSMatthew Dillon 			(*histogram_ary)[res].tid = tid_beg +
5853d7b2393SMatthew Dillon 					i * (tid_end - tid_beg) /
5863d7b2393SMatthew Dillon 					HIST_COUNT;
587*ced4470dSMatthew Dillon 			(*histogram_ary)[res].bytes = 0;
588*ced4470dSMatthew Dillon 
5893d7b2393SMatthew Dillon 		}
590*ced4470dSMatthew Dillon 		(*histogram_ary)[res].bytes += tid_bytes[i];
5913d7b2393SMatthew Dillon 	}
592*ced4470dSMatthew Dillon 	++res;
593*ced4470dSMatthew Dillon 	(*histogram_ary)[res].tid = tid_end;
594*ced4470dSMatthew Dillon 	(*histogram_ary)[res].bytes = -1;
595*ced4470dSMatthew Dillon 
5963d7b2393SMatthew Dillon 	if (VerboseOpt > 1)
5973d7b2393SMatthew Dillon 		fprintf(stderr, "\n");	/* newline after ... */
598*ced4470dSMatthew Dillon 	assert(res <= HIST_COUNT);
599*ced4470dSMatthew Dillon 	fprintf(stderr, "Prescan %d chunks, total %ju MBytes (\n",
6003d7b2393SMatthew Dillon 		res, (uintmax_t)total / (1024 * 1024));
601*ced4470dSMatthew Dillon 	for (i = 0; i < res && i < 3; ++i) {
602*ced4470dSMatthew Dillon 		if (i)
603*ced4470dSMatthew Dillon 			fprintf(stderr, ", ");
604*ced4470dSMatthew Dillon 		fprintf(stderr, "%ju", (uintmax_t)(*histogram_ary)[i].bytes);
605*ced4470dSMatthew Dillon 	}
606*ced4470dSMatthew Dillon 	if (i < res)
607*ced4470dSMatthew Dillon 		fprintf(stderr, ", ...");
608*ced4470dSMatthew Dillon 	fprintf(stderr, ")\n");
609*ced4470dSMatthew Dillon 
610*ced4470dSMatthew Dillon 	free(tid_bytes);
6113d7b2393SMatthew Dillon 	return(res);
612e7f926a5SMatthew Dillon }
613e7f926a5SMatthew Dillon 
61401a72c9fSMatthew Dillon static void
61501a72c9fSMatthew Dillon create_pfs(const char *filesystem, uuid_t *s_uuid)
61601a72c9fSMatthew Dillon {
617f414d101SSascha Wildner 	if (ForceYesOpt == 1) {
61807485271SMichael Neumann 		fprintf(stderr, "PFS slave %s does not exist. "
61907485271SMichael Neumann 			"Auto create new slave PFS!\n", filesystem);
62007485271SMichael Neumann 
621f414d101SSascha Wildner 	} else {
62201a72c9fSMatthew Dillon 		fprintf(stderr, "PFS slave %s does not exist.\n"
62301a72c9fSMatthew Dillon 			"Do you want to create a new slave PFS? (yes|no) ",
62401a72c9fSMatthew Dillon 			filesystem);
62501a72c9fSMatthew Dillon 		fflush(stderr);
62601a72c9fSMatthew Dillon 		if (getyn() != 1) {
62701a72c9fSMatthew Dillon 			fprintf(stderr, "Aborting operation\n");
62801a72c9fSMatthew Dillon 			exit(1);
62901a72c9fSMatthew Dillon 		}
63007485271SMichael Neumann 	}
63101a72c9fSMatthew Dillon 
63201a72c9fSMatthew Dillon 	u_int32_t status;
63301a72c9fSMatthew Dillon 	char *shared_uuid = NULL;
63401a72c9fSMatthew Dillon 	uuid_to_string(s_uuid, &shared_uuid, &status);
63501a72c9fSMatthew Dillon 
63601a72c9fSMatthew Dillon 	char *cmd = NULL;
63701a72c9fSMatthew Dillon 	asprintf(&cmd, "/sbin/hammer pfs-slave '%s' shared-uuid=%s 1>&2",
63801a72c9fSMatthew Dillon 		 filesystem, shared_uuid);
63901a72c9fSMatthew Dillon 	free(shared_uuid);
64001a72c9fSMatthew Dillon 
64101a72c9fSMatthew Dillon 	if (cmd == NULL) {
64201a72c9fSMatthew Dillon 		fprintf(stderr, "Failed to alloc memory\n");
64301a72c9fSMatthew Dillon 		exit(1);
64401a72c9fSMatthew Dillon 	}
64501a72c9fSMatthew Dillon 	if (system(cmd) != 0) {
64601a72c9fSMatthew Dillon 		fprintf(stderr, "Failed to create PFS\n");
64701a72c9fSMatthew Dillon 	}
64801a72c9fSMatthew Dillon 	free(cmd);
64901a72c9fSMatthew Dillon }
65001a72c9fSMatthew Dillon 
65117dd83bcSMatthew Dillon /*
65217dd83bcSMatthew Dillon  * Pipe the mirroring data stream on stdin to the HAMMER VFS, adding
65317dd83bcSMatthew Dillon  * some additional packet types to negotiate TID ranges and to verify
65417dd83bcSMatthew Dillon  * completion.  The HAMMER VFS does most of the work.
65517dd83bcSMatthew Dillon  *
65617dd83bcSMatthew Dillon  * It is important to note that the mirror.key_{beg,end} range must
65717dd83bcSMatthew Dillon  * match the ranged used by the original.  For now both sides use
65817dd83bcSMatthew Dillon  * range the entire key space.
65917dd83bcSMatthew Dillon  *
66017dd83bcSMatthew Dillon  * It is even more important that the records in the stream conform
66117dd83bcSMatthew Dillon  * to the TID range also supplied in the stream.  The HAMMER VFS will
66217dd83bcSMatthew Dillon  * use the REC, PASS, and SKIP record types to track the portions of
66317dd83bcSMatthew Dillon  * the B-Tree being scanned in order to be able to proactively delete
66417dd83bcSMatthew Dillon  * records on the target within those active areas that are not mentioned
66517dd83bcSMatthew Dillon  * by the source.
66617dd83bcSMatthew Dillon  *
66717dd83bcSMatthew Dillon  * The mirror.key_cur field is used by the VFS to do this tracking.  It
66817dd83bcSMatthew Dillon  * must be initialized to key_beg but then is persistently updated by
66917dd83bcSMatthew Dillon  * the HAMMER VFS on each successive ioctl() call.  If you blow up this
67017dd83bcSMatthew Dillon  * field you will blow up the mirror target, possibly to the point of
67117dd83bcSMatthew Dillon  * deleting everything.  As a safety measure the HAMMER VFS simply marks
67217dd83bcSMatthew Dillon  * the records that the source has destroyed as deleted on the target,
67317dd83bcSMatthew Dillon  * and normal pruning operations will deal with their final disposition
67417dd83bcSMatthew Dillon  * at some later time.
67517dd83bcSMatthew Dillon  */
676a7fbbf91SMatthew Dillon void
677a7fbbf91SMatthew Dillon hammer_cmd_mirror_write(char **av, int ac)
678a7fbbf91SMatthew Dillon {
679a7fbbf91SMatthew Dillon 	struct hammer_ioc_mirror_rw mirror;
680a7fbbf91SMatthew Dillon 	const char *filesystem;
681a7fbbf91SMatthew Dillon 	char *buf = malloc(SERIALBUF_SIZE);
682d4e5b69bSMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
68317dd83bcSMatthew Dillon 	struct hammer_ioc_mrecord_head pickup;
684243ca327SMatthew Dillon 	struct hammer_ioc_synctid synctid;
68517dd83bcSMatthew Dillon 	union hammer_ioc_mrecord_any mrec_tmp;
68617dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
68701a72c9fSMatthew Dillon 	struct stat st;
688243ca327SMatthew Dillon 	int error;
689243ca327SMatthew Dillon 	int fd;
69048eadef9SMatthew Dillon 	int n;
691a7fbbf91SMatthew Dillon 
69234bb69d8SThomas Nikolajsen 	if (ac != 1)
693a7fbbf91SMatthew Dillon 		mirror_usage(1);
694a7fbbf91SMatthew Dillon 	filesystem = av[0];
695a7fbbf91SMatthew Dillon 
69648eadef9SMatthew Dillon 	pickup.signature = 0;
69748eadef9SMatthew Dillon 	pickup.type = 0;
69848eadef9SMatthew Dillon 
69948eadef9SMatthew Dillon again:
700a7fbbf91SMatthew Dillon 	bzero(&mirror, sizeof(mirror));
701a7fbbf91SMatthew Dillon 	hammer_key_beg_init(&mirror.key_beg);
702a7fbbf91SMatthew Dillon 	hammer_key_end_init(&mirror.key_end);
70317dd83bcSMatthew Dillon 	mirror.key_end = mirror.key_beg;
704a7fbbf91SMatthew Dillon 
70501a72c9fSMatthew Dillon 	/*
70601a72c9fSMatthew Dillon 	 * Read initial packet
70701a72c9fSMatthew Dillon 	 */
70801a72c9fSMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
70901a72c9fSMatthew Dillon 	if (mrec == NULL) {
71001a72c9fSMatthew Dillon 		if (error == 0)
71101a72c9fSMatthew Dillon 			fprintf(stderr, "validate_mrec_header: short read\n");
71201a72c9fSMatthew Dillon 		exit(1);
71301a72c9fSMatthew Dillon 	}
71401a72c9fSMatthew Dillon 	/*
71501a72c9fSMatthew Dillon 	 * Validate packet
71601a72c9fSMatthew Dillon 	 */
71701a72c9fSMatthew Dillon 	if (mrec->head.type == HAMMER_MREC_TYPE_TERM) {
71801a72c9fSMatthew Dillon 		return;
71901a72c9fSMatthew Dillon 	}
72001a72c9fSMatthew Dillon 	if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) {
72101a72c9fSMatthew Dillon 		fprintf(stderr, "validate_mrec_header: did not get expected "
72201a72c9fSMatthew Dillon 				"PFSD record type\n");
72301a72c9fSMatthew Dillon 		exit(1);
72401a72c9fSMatthew Dillon 	}
72501a72c9fSMatthew Dillon 	if (mrec->head.rec_size != sizeof(mrec->pfs)) {
72601a72c9fSMatthew Dillon 		fprintf(stderr, "validate_mrec_header: unexpected payload "
72701a72c9fSMatthew Dillon 				"size\n");
72801a72c9fSMatthew Dillon 		exit(1);
72901a72c9fSMatthew Dillon 	}
73001a72c9fSMatthew Dillon 
73101a72c9fSMatthew Dillon 	/*
73201a72c9fSMatthew Dillon 	 * Create slave PFS if it doesn't yet exist
73301a72c9fSMatthew Dillon 	 */
73401a72c9fSMatthew Dillon 	if (lstat(filesystem, &st) != 0) {
73501a72c9fSMatthew Dillon 		create_pfs(filesystem, &mrec->pfs.pfsd.shared_uuid);
73601a72c9fSMatthew Dillon 	}
73701a72c9fSMatthew Dillon 	free(mrec);
73801a72c9fSMatthew Dillon 	mrec = NULL;
73901a72c9fSMatthew Dillon 
740d4e5b69bSMatthew Dillon 	fd = getpfs(&pfs, filesystem);
741a7fbbf91SMatthew Dillon 
742243ca327SMatthew Dillon 	/*
743d4e5b69bSMatthew Dillon 	 * In two-way mode the target writes out a PFS packet first.
744d4e5b69bSMatthew Dillon 	 * The source uses our tid_end as its tid_beg by default,
745d4e5b69bSMatthew Dillon 	 * picking up where it left off.
746243ca327SMatthew Dillon 	 */
747d4e5b69bSMatthew Dillon 	mirror.tid_beg = 0;
748d4e5b69bSMatthew Dillon 	if (TwoWayPipeOpt) {
749e7f926a5SMatthew Dillon 		generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp);
750e7f926a5SMatthew Dillon 		if (mirror.tid_beg < mrec_tmp.pfs.pfsd.sync_beg_tid)
751e7f926a5SMatthew Dillon 			mirror.tid_beg = mrec_tmp.pfs.pfsd.sync_beg_tid;
752e7f926a5SMatthew Dillon 		mirror.tid_end = mrec_tmp.pfs.pfsd.sync_end_tid;
753e7f926a5SMatthew Dillon 		write_mrecord(1, HAMMER_MREC_TYPE_PFSD,
754e7f926a5SMatthew Dillon 			      &mrec_tmp, sizeof(mrec_tmp.pfs));
755d4e5b69bSMatthew Dillon 	}
756d4e5b69bSMatthew Dillon 
757d4e5b69bSMatthew Dillon 	/*
75817dd83bcSMatthew Dillon 	 * Read and process the PFS header.  The source informs us of
75917dd83bcSMatthew Dillon 	 * the TID range the stream represents.
760d4e5b69bSMatthew Dillon 	 */
76148eadef9SMatthew Dillon 	n = validate_mrec_header(fd, 0, 1, pfs.pfs_id, &pickup,
762d4e5b69bSMatthew Dillon 				 &mirror.tid_beg, &mirror.tid_end);
76348eadef9SMatthew Dillon 	if (n < 0) {	/* got TERM record */
76448eadef9SMatthew Dillon 		relpfs(fd, &pfs);
76548eadef9SMatthew Dillon 		return;
76648eadef9SMatthew Dillon 	}
76734ebae70SMatthew Dillon 
768a7fbbf91SMatthew Dillon 	mirror.ubuf = buf;
769a7fbbf91SMatthew Dillon 	mirror.size = SERIALBUF_SIZE;
770a7fbbf91SMatthew Dillon 
771243ca327SMatthew Dillon 	/*
77217dd83bcSMatthew Dillon 	 * Read and process bulk records (REC, PASS, and SKIP types).
77317dd83bcSMatthew Dillon 	 *
77417dd83bcSMatthew Dillon 	 * On your life, do NOT mess with mirror.key_cur or your mirror
77517dd83bcSMatthew Dillon 	 * target may become history.
776243ca327SMatthew Dillon 	 */
777a7fbbf91SMatthew Dillon 	for (;;) {
778a7fbbf91SMatthew Dillon 		mirror.count = 0;
779d4e5b69bSMatthew Dillon 		mirror.pfs_id = pfs.pfs_id;
780d4e5b69bSMatthew Dillon 		mirror.shared_uuid = pfs.ondisk->shared_uuid;
781a7fbbf91SMatthew Dillon 		mirror.size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup);
782a7fbbf91SMatthew Dillon 		if (mirror.size <= 0)
783a7fbbf91SMatthew Dillon 			break;
784a7fbbf91SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_MIRROR_WRITE, &mirror) < 0) {
785a7fbbf91SMatthew Dillon 			fprintf(stderr, "Mirror-write %s failed: %s\n",
786a7fbbf91SMatthew Dillon 				filesystem, strerror(errno));
787a7fbbf91SMatthew Dillon 			exit(1);
788a7fbbf91SMatthew Dillon 		}
78917dd83bcSMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) {
79017dd83bcSMatthew Dillon 			fprintf(stderr,
79117dd83bcSMatthew Dillon 				"Mirror-write %s fatal error %d\n",
79217dd83bcSMatthew Dillon 				filesystem, mirror.head.error);
79317dd83bcSMatthew Dillon 			exit(1);
79417dd83bcSMatthew Dillon 		}
795243ca327SMatthew Dillon #if 0
796a7fbbf91SMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_INTR) {
797a7fbbf91SMatthew Dillon 			fprintf(stderr,
798a7fbbf91SMatthew Dillon 				"Mirror-write %s interrupted by timer at"
799243ca327SMatthew Dillon 				" %016llx\n",
800a7fbbf91SMatthew Dillon 				filesystem,
801243ca327SMatthew Dillon 				mirror.key_cur.obj_id);
802a7fbbf91SMatthew Dillon 			exit(0);
803a7fbbf91SMatthew Dillon 		}
804243ca327SMatthew Dillon #endif
805a7fbbf91SMatthew Dillon 	}
806243ca327SMatthew Dillon 
807243ca327SMatthew Dillon 	/*
808243ca327SMatthew Dillon 	 * Read and process the termination sync record.
809243ca327SMatthew Dillon 	 */
810243ca327SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
8119dc76cb1SMatthew Dillon 
8129dc76cb1SMatthew Dillon 	if (mrec && mrec->head.type == HAMMER_MREC_TYPE_TERM) {
81348eadef9SMatthew Dillon 		fprintf(stderr, "Mirror-write: received termination request\n");
81448eadef9SMatthew Dillon 		free(mrec);
8159dc76cb1SMatthew Dillon 		return;
8169dc76cb1SMatthew Dillon 	}
8179dc76cb1SMatthew Dillon 
81817dd83bcSMatthew Dillon 	if (mrec == NULL ||
81948eadef9SMatthew Dillon 	    (mrec->head.type != HAMMER_MREC_TYPE_SYNC &&
82048eadef9SMatthew Dillon 	     mrec->head.type != HAMMER_MREC_TYPE_IDLE) ||
82117dd83bcSMatthew Dillon 	    mrec->head.rec_size != sizeof(mrec->sync)) {
822243ca327SMatthew Dillon 		fprintf(stderr, "Mirror-write %s: Did not get termination "
82317dd83bcSMatthew Dillon 				"sync record, or rec_size is wrong rt=%d\n",
82417dd83bcSMatthew Dillon 				filesystem, mrec->head.type);
825da44aa75SMatthew Dillon 		exit(1);
826243ca327SMatthew Dillon 	}
827243ca327SMatthew Dillon 
828243ca327SMatthew Dillon 	/*
829243ca327SMatthew Dillon 	 * Update the PFS info on the target so the user has visibility
83048eadef9SMatthew Dillon 	 * into the new snapshot, and sync the target filesystem.
831243ca327SMatthew Dillon 	 */
83248eadef9SMatthew Dillon 	if (mrec->head.type == HAMMER_MREC_TYPE_SYNC) {
833d4e5b69bSMatthew Dillon 		update_pfs_snapshot(fd, mirror.tid_end, pfs.pfs_id);
834243ca327SMatthew Dillon 
835243ca327SMatthew Dillon 		bzero(&synctid, sizeof(synctid));
836243ca327SMatthew Dillon 		synctid.op = HAMMER_SYNCTID_SYNC2;
837243ca327SMatthew Dillon 		ioctl(fd, HAMMERIOC_SYNCTID, &synctid);
838243ca327SMatthew Dillon 
83948eadef9SMatthew Dillon 		if (VerboseOpt >= 2) {
84048eadef9SMatthew Dillon 			fprintf(stderr, "Mirror-write %s: succeeded\n",
84148eadef9SMatthew Dillon 				filesystem);
84248eadef9SMatthew Dillon 		}
84348eadef9SMatthew Dillon 	}
84448eadef9SMatthew Dillon 
84548eadef9SMatthew Dillon 	free(mrec);
84648eadef9SMatthew Dillon 	mrec = NULL;
847243ca327SMatthew Dillon 
848243ca327SMatthew Dillon 	/*
849243ca327SMatthew Dillon 	 * Report back to the originator.
850243ca327SMatthew Dillon 	 */
851243ca327SMatthew Dillon 	if (TwoWayPipeOpt) {
85217dd83bcSMatthew Dillon 		mrec_tmp.update.tid = mirror.tid_end;
853243ca327SMatthew Dillon 		write_mrecord(1, HAMMER_MREC_TYPE_UPDATE,
85417dd83bcSMatthew Dillon 			      &mrec_tmp, sizeof(mrec_tmp.update));
855243ca327SMatthew Dillon 	} else {
856a276dc6bSMatthew Dillon 		printf("Source can update synctid to 0x%016jx\n",
857a276dc6bSMatthew Dillon 		       (uintmax_t)mirror.tid_end);
858243ca327SMatthew Dillon 	}
85948eadef9SMatthew Dillon 	relpfs(fd, &pfs);
86048eadef9SMatthew Dillon 	goto again;
861243ca327SMatthew Dillon }
862243ca327SMatthew Dillon 
863243ca327SMatthew Dillon void
864243ca327SMatthew Dillon hammer_cmd_mirror_dump(void)
865243ca327SMatthew Dillon {
866243ca327SMatthew Dillon 	char *buf = malloc(SERIALBUF_SIZE);
86717dd83bcSMatthew Dillon 	struct hammer_ioc_mrecord_head pickup;
86817dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
869243ca327SMatthew Dillon 	int error;
870243ca327SMatthew Dillon 	int size;
87117dd83bcSMatthew Dillon 	int offset;
87217dd83bcSMatthew Dillon 	int bytes;
873243ca327SMatthew Dillon 
874243ca327SMatthew Dillon 	/*
875243ca327SMatthew Dillon 	 * Read and process the PFS header
876243ca327SMatthew Dillon 	 */
877243ca327SMatthew Dillon 	pickup.signature = 0;
878243ca327SMatthew Dillon 	pickup.type = 0;
879243ca327SMatthew Dillon 
880243ca327SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
881243ca327SMatthew Dillon 
882243ca327SMatthew Dillon 	/*
883243ca327SMatthew Dillon 	 * Read and process bulk records
884243ca327SMatthew Dillon 	 */
885243ca327SMatthew Dillon 	for (;;) {
886243ca327SMatthew Dillon 		size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup);
887243ca327SMatthew Dillon 		if (size <= 0)
888243ca327SMatthew Dillon 			break;
88917dd83bcSMatthew Dillon 		offset = 0;
89017dd83bcSMatthew Dillon 		while (offset < size) {
89117dd83bcSMatthew Dillon 			mrec = (void *)((char *)buf + offset);
89217dd83bcSMatthew Dillon 			bytes = HAMMER_HEAD_DOALIGN(mrec->head.rec_size);
89317dd83bcSMatthew Dillon 			if (offset + bytes > size) {
89417dd83bcSMatthew Dillon 				fprintf(stderr, "Misaligned record\n");
89517dd83bcSMatthew Dillon 				exit(1);
89617dd83bcSMatthew Dillon 			}
89717dd83bcSMatthew Dillon 
89885a8e8a7SMatthew Dillon 			switch(mrec->head.type & HAMMER_MRECF_TYPE_MASK) {
89985a8e8a7SMatthew Dillon 			case HAMMER_MREC_TYPE_REC_BADCRC:
90017dd83bcSMatthew Dillon 			case HAMMER_MREC_TYPE_REC:
901a276dc6bSMatthew Dillon 				printf("Record obj=%016jx key=%016jx "
90285a8e8a7SMatthew Dillon 				       "rt=%02x ot=%02x",
903a276dc6bSMatthew Dillon 					(uintmax_t)mrec->rec.leaf.base.obj_id,
904a276dc6bSMatthew Dillon 					(uintmax_t)mrec->rec.leaf.base.key,
90517dd83bcSMatthew Dillon 					mrec->rec.leaf.base.rec_type,
90617dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_type);
90785a8e8a7SMatthew Dillon 				if (mrec->head.type ==
90885a8e8a7SMatthew Dillon 				    HAMMER_MREC_TYPE_REC_BADCRC) {
90985a8e8a7SMatthew Dillon 					printf(" (BAD CRC)");
91085a8e8a7SMatthew Dillon 				}
91185a8e8a7SMatthew Dillon 				printf("\n");
912a276dc6bSMatthew Dillon 				printf("       tids %016jx:%016jx data=%d\n",
913a276dc6bSMatthew Dillon 				    (uintmax_t)mrec->rec.leaf.base.create_tid,
914a276dc6bSMatthew Dillon 				    (uintmax_t)mrec->rec.leaf.base.delete_tid,
91517dd83bcSMatthew Dillon 				    mrec->rec.leaf.data_len);
91617dd83bcSMatthew Dillon 				break;
91717dd83bcSMatthew Dillon 			case HAMMER_MREC_TYPE_PASS:
918a276dc6bSMatthew Dillon 				printf("Pass   obj=%016jx key=%016jx "
91917dd83bcSMatthew Dillon 				       "rt=%02x ot=%02x\n",
920a276dc6bSMatthew Dillon 					(uintmax_t)mrec->rec.leaf.base.obj_id,
921a276dc6bSMatthew Dillon 					(uintmax_t)mrec->rec.leaf.base.key,
92217dd83bcSMatthew Dillon 					mrec->rec.leaf.base.rec_type,
92317dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_type);
924a276dc6bSMatthew Dillon 				printf("       tids %016jx:%016jx data=%d\n",
925a276dc6bSMatthew Dillon 				    (uintmax_t)mrec->rec.leaf.base.create_tid,
926a276dc6bSMatthew Dillon 				    (uintmax_t)mrec->rec.leaf.base.delete_tid,
92717dd83bcSMatthew Dillon 					mrec->rec.leaf.data_len);
92817dd83bcSMatthew Dillon 				break;
92917dd83bcSMatthew Dillon 			case HAMMER_MREC_TYPE_SKIP:
930a276dc6bSMatthew Dillon 				printf("Skip   obj=%016jx key=%016jx rt=%02x to\n"
931a276dc6bSMatthew Dillon 				       "       obj=%016jx key=%016jx rt=%02x\n",
932a276dc6bSMatthew Dillon 				       (uintmax_t)mrec->skip.skip_beg.obj_id,
933a276dc6bSMatthew Dillon 				       (uintmax_t)mrec->skip.skip_beg.key,
93417dd83bcSMatthew Dillon 				       mrec->skip.skip_beg.rec_type,
935a276dc6bSMatthew Dillon 				       (uintmax_t)mrec->skip.skip_end.obj_id,
936a276dc6bSMatthew Dillon 				       (uintmax_t)mrec->skip.skip_end.key,
93717dd83bcSMatthew Dillon 				       mrec->skip.skip_end.rec_type);
93817dd83bcSMatthew Dillon 			default:
93917dd83bcSMatthew Dillon 				break;
94017dd83bcSMatthew Dillon 			}
94117dd83bcSMatthew Dillon 			offset += bytes;
942243ca327SMatthew Dillon 		}
943243ca327SMatthew Dillon 	}
944243ca327SMatthew Dillon 
945243ca327SMatthew Dillon 	/*
946243ca327SMatthew Dillon 	 * Read and process the termination sync record.
947243ca327SMatthew Dillon 	 */
948243ca327SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
94948eadef9SMatthew Dillon 	if (mrec == NULL ||
95048eadef9SMatthew Dillon 	    (mrec->head.type != HAMMER_MREC_TYPE_SYNC &&
95148eadef9SMatthew Dillon 	     mrec->head.type != HAMMER_MREC_TYPE_IDLE)
95248eadef9SMatthew Dillon 	 ) {
953243ca327SMatthew Dillon 		fprintf(stderr, "Mirror-dump: Did not get termination "
954243ca327SMatthew Dillon 				"sync record\n");
955243ca327SMatthew Dillon 	}
956a7fbbf91SMatthew Dillon }
957a7fbbf91SMatthew Dillon 
958a7fbbf91SMatthew Dillon void
95948eadef9SMatthew Dillon hammer_cmd_mirror_copy(char **av, int ac, int streaming)
960a7fbbf91SMatthew Dillon {
96134ebae70SMatthew Dillon 	pid_t pid1;
96234ebae70SMatthew Dillon 	pid_t pid2;
96334ebae70SMatthew Dillon 	int fds[2];
964243ca327SMatthew Dillon 	const char *xav[16];
965243ca327SMatthew Dillon 	char tbuf[16];
96634ebae70SMatthew Dillon 	char *ptr;
967243ca327SMatthew Dillon 	int xac;
96834ebae70SMatthew Dillon 
96934ebae70SMatthew Dillon 	if (ac != 2)
97034ebae70SMatthew Dillon 		mirror_usage(1);
97134ebae70SMatthew Dillon 
972e7f926a5SMatthew Dillon 	TwoWayPipeOpt = 1;
9730bd7a37cSMatthew Dillon 	signal(SIGPIPE, SIG_IGN);
974e7f926a5SMatthew Dillon 
975e7f926a5SMatthew Dillon again:
97634ebae70SMatthew Dillon 	if (pipe(fds) < 0) {
97734ebae70SMatthew Dillon 		perror("pipe");
97834ebae70SMatthew Dillon 		exit(1);
97934ebae70SMatthew Dillon 	}
98034ebae70SMatthew Dillon 
98134ebae70SMatthew Dillon 	/*
98234ebae70SMatthew Dillon 	 * Source
98334ebae70SMatthew Dillon 	 */
98434ebae70SMatthew Dillon 	if ((pid1 = fork()) == 0) {
9850bd7a37cSMatthew Dillon 		signal(SIGPIPE, SIG_DFL);
98634ebae70SMatthew Dillon 		dup2(fds[0], 0);
98734ebae70SMatthew Dillon 		dup2(fds[0], 1);
98834ebae70SMatthew Dillon 		close(fds[0]);
98934ebae70SMatthew Dillon 		close(fds[1]);
99034ebae70SMatthew Dillon 		if ((ptr = strchr(av[0], ':')) != NULL) {
99134ebae70SMatthew Dillon 			*ptr++ = 0;
992243ca327SMatthew Dillon 			xac = 0;
993243ca327SMatthew Dillon 			xav[xac++] = "ssh";
9943a998207SMatthew Dillon 			if (CompressOpt)
9953a998207SMatthew Dillon 				xav[xac++] = "-C";
9966c45ca3eSMatthew Dillon 			if (SshPort) {
9976c45ca3eSMatthew Dillon 				xav[xac++] = "-p";
9986c45ca3eSMatthew Dillon 				xav[xac++] = SshPort;
9996c45ca3eSMatthew Dillon 			}
1000243ca327SMatthew Dillon 			xav[xac++] = av[0];
1001243ca327SMatthew Dillon 			xav[xac++] = "hammer";
100248eadef9SMatthew Dillon 
100348eadef9SMatthew Dillon 			switch(VerboseOpt) {
100448eadef9SMatthew Dillon 			case 0:
100548eadef9SMatthew Dillon 				break;
100648eadef9SMatthew Dillon 			case 1:
1007243ca327SMatthew Dillon 				xav[xac++] = "-v";
100848eadef9SMatthew Dillon 				break;
100948eadef9SMatthew Dillon 			case 2:
101048eadef9SMatthew Dillon 				xav[xac++] = "-vv";
101148eadef9SMatthew Dillon 				break;
101248eadef9SMatthew Dillon 			default:
101348eadef9SMatthew Dillon 				xav[xac++] = "-vvv";
101448eadef9SMatthew Dillon 				break;
101548eadef9SMatthew Dillon 			}
101607485271SMichael Neumann 			if (ForceYesOpt) {
101707485271SMichael Neumann 				xav[xac++] = "-y";
101807485271SMichael Neumann 			}
1019243ca327SMatthew Dillon 			xav[xac++] = "-2";
1020243ca327SMatthew Dillon 			if (TimeoutOpt) {
1021243ca327SMatthew Dillon 				snprintf(tbuf, sizeof(tbuf), "%d", TimeoutOpt);
1022243ca327SMatthew Dillon 				xav[xac++] = "-t";
1023243ca327SMatthew Dillon 				xav[xac++] = tbuf;
1024243ca327SMatthew Dillon 			}
102548eadef9SMatthew Dillon 			if (streaming)
1026901f434aSMatthew Dillon 				xav[xac++] = "mirror-read-stream";
102748eadef9SMatthew Dillon 			else
1028243ca327SMatthew Dillon 				xav[xac++] = "mirror-read";
1029243ca327SMatthew Dillon 			xav[xac++] = ptr;
1030243ca327SMatthew Dillon 			xav[xac++] = NULL;
1031243ca327SMatthew Dillon 			execv("/usr/bin/ssh", (void *)xav);
103234ebae70SMatthew Dillon 		} else {
103348eadef9SMatthew Dillon 			hammer_cmd_mirror_read(av, 1, streaming);
1034243ca327SMatthew Dillon 			fflush(stdout);
1035243ca327SMatthew Dillon 			fflush(stderr);
103634ebae70SMatthew Dillon 		}
103753d93cc7SMatthew Dillon 		_exit(1);
103834ebae70SMatthew Dillon 	}
103934ebae70SMatthew Dillon 
104034ebae70SMatthew Dillon 	/*
104134ebae70SMatthew Dillon 	 * Target
104234ebae70SMatthew Dillon 	 */
104334ebae70SMatthew Dillon 	if ((pid2 = fork()) == 0) {
10440bd7a37cSMatthew Dillon 		signal(SIGPIPE, SIG_DFL);
104534ebae70SMatthew Dillon 		dup2(fds[1], 0);
104634ebae70SMatthew Dillon 		dup2(fds[1], 1);
104734ebae70SMatthew Dillon 		close(fds[0]);
104834ebae70SMatthew Dillon 		close(fds[1]);
104934ebae70SMatthew Dillon 		if ((ptr = strchr(av[1], ':')) != NULL) {
105034ebae70SMatthew Dillon 			*ptr++ = 0;
1051243ca327SMatthew Dillon 			xac = 0;
1052243ca327SMatthew Dillon 			xav[xac++] = "ssh";
10533a998207SMatthew Dillon 			if (CompressOpt)
10543a998207SMatthew Dillon 				xav[xac++] = "-C";
10556c45ca3eSMatthew Dillon 			if (SshPort) {
10566c45ca3eSMatthew Dillon 				xav[xac++] = "-p";
10576c45ca3eSMatthew Dillon 				xav[xac++] = SshPort;
10586c45ca3eSMatthew Dillon 			}
1059243ca327SMatthew Dillon 			xav[xac++] = av[1];
1060243ca327SMatthew Dillon 			xav[xac++] = "hammer";
106148eadef9SMatthew Dillon 
106248eadef9SMatthew Dillon 			switch(VerboseOpt) {
106348eadef9SMatthew Dillon 			case 0:
106448eadef9SMatthew Dillon 				break;
106548eadef9SMatthew Dillon 			case 1:
1066243ca327SMatthew Dillon 				xav[xac++] = "-v";
106748eadef9SMatthew Dillon 				break;
106848eadef9SMatthew Dillon 			case 2:
106948eadef9SMatthew Dillon 				xav[xac++] = "-vv";
107048eadef9SMatthew Dillon 				break;
107148eadef9SMatthew Dillon 			default:
107248eadef9SMatthew Dillon 				xav[xac++] = "-vvv";
107348eadef9SMatthew Dillon 				break;
107448eadef9SMatthew Dillon 			}
107507485271SMichael Neumann 			if (ForceYesOpt) {
107607485271SMichael Neumann 				xav[xac++] = "-y";
107707485271SMichael Neumann 			}
1078243ca327SMatthew Dillon 			xav[xac++] = "-2";
1079243ca327SMatthew Dillon 			xav[xac++] = "mirror-write";
1080243ca327SMatthew Dillon 			xav[xac++] = ptr;
1081243ca327SMatthew Dillon 			xav[xac++] = NULL;
1082243ca327SMatthew Dillon 			execv("/usr/bin/ssh", (void *)xav);
108334ebae70SMatthew Dillon 		} else {
108434ebae70SMatthew Dillon 			hammer_cmd_mirror_write(av + 1, 1);
1085243ca327SMatthew Dillon 			fflush(stdout);
1086243ca327SMatthew Dillon 			fflush(stderr);
108734ebae70SMatthew Dillon 		}
108853d93cc7SMatthew Dillon 		_exit(1);
108934ebae70SMatthew Dillon 	}
109034ebae70SMatthew Dillon 	close(fds[0]);
109134ebae70SMatthew Dillon 	close(fds[1]);
109234ebae70SMatthew Dillon 
109334ebae70SMatthew Dillon 	while (waitpid(pid1, NULL, 0) <= 0)
109434ebae70SMatthew Dillon 		;
109534ebae70SMatthew Dillon 	while (waitpid(pid2, NULL, 0) <= 0)
109634ebae70SMatthew Dillon 		;
1097e7f926a5SMatthew Dillon 
1098e7f926a5SMatthew Dillon 	/*
1099e7f926a5SMatthew Dillon 	 * If the link is lost restart
1100e7f926a5SMatthew Dillon 	 */
1101e7f926a5SMatthew Dillon 	if (streaming) {
1102e7f926a5SMatthew Dillon 		if (VerboseOpt) {
1103e7f926a5SMatthew Dillon 			fprintf(stderr, "\nLost Link\n");
1104e7f926a5SMatthew Dillon 			fflush(stderr);
1105e7f926a5SMatthew Dillon 		}
11060bd7a37cSMatthew Dillon 		sleep(15 + DelayOpt);
1107e7f926a5SMatthew Dillon 		goto again;
1108e7f926a5SMatthew Dillon 	}
1109e7f926a5SMatthew Dillon 
1110a7fbbf91SMatthew Dillon }
1111a7fbbf91SMatthew Dillon 
1112243ca327SMatthew Dillon /*
1113243ca327SMatthew Dillon  * Read and return multiple mrecords
1114243ca327SMatthew Dillon  */
1115a7fbbf91SMatthew Dillon static int
111617dd83bcSMatthew Dillon read_mrecords(int fd, char *buf, u_int size, hammer_ioc_mrecord_head_t pickup)
1117a7fbbf91SMatthew Dillon {
111817dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
1119a7fbbf91SMatthew Dillon 	u_int count;
1120a7fbbf91SMatthew Dillon 	size_t n;
1121a7fbbf91SMatthew Dillon 	size_t i;
112217dd83bcSMatthew Dillon 	size_t bytes;
112385a8e8a7SMatthew Dillon 	int type;
1124a7fbbf91SMatthew Dillon 
1125a7fbbf91SMatthew Dillon 	count = 0;
1126a7fbbf91SMatthew Dillon 	while (size - count >= HAMMER_MREC_HEADSIZE) {
1127a7fbbf91SMatthew Dillon 		/*
1128a7fbbf91SMatthew Dillon 		 * Cached the record header in case we run out of buffer
1129a7fbbf91SMatthew Dillon 		 * space.
1130a7fbbf91SMatthew Dillon 		 */
113117dd83bcSMatthew Dillon 		fflush(stdout);
1132a7fbbf91SMatthew Dillon 		if (pickup->signature == 0) {
1133a7fbbf91SMatthew Dillon 			for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) {
1134a7fbbf91SMatthew Dillon 				i = read(fd, (char *)pickup + n,
1135a7fbbf91SMatthew Dillon 					 HAMMER_MREC_HEADSIZE - n);
1136a7fbbf91SMatthew Dillon 				if (i <= 0)
1137a7fbbf91SMatthew Dillon 					break;
1138a7fbbf91SMatthew Dillon 			}
1139a7fbbf91SMatthew Dillon 			if (n == 0)
1140a7fbbf91SMatthew Dillon 				break;
1141a7fbbf91SMatthew Dillon 			if (n != HAMMER_MREC_HEADSIZE) {
1142a7fbbf91SMatthew Dillon 				fprintf(stderr, "read_mrecords: short read on pipe\n");
1143a7fbbf91SMatthew Dillon 				exit(1);
1144a7fbbf91SMatthew Dillon 			}
1145a7fbbf91SMatthew Dillon 			if (pickup->signature != HAMMER_IOC_MIRROR_SIGNATURE) {
114634bb69d8SThomas Nikolajsen 				fprintf(stderr, "read_mrecords: malformed record on pipe, "
114734bb69d8SThomas Nikolajsen 					"bad signature\n");
1148a7fbbf91SMatthew Dillon 				exit(1);
1149a7fbbf91SMatthew Dillon 			}
1150a7fbbf91SMatthew Dillon 		}
1151a7fbbf91SMatthew Dillon 		if (pickup->rec_size < HAMMER_MREC_HEADSIZE ||
115217dd83bcSMatthew Dillon 		    pickup->rec_size > sizeof(*mrec) + HAMMER_XBUFSIZE) {
115334bb69d8SThomas Nikolajsen 			fprintf(stderr, "read_mrecords: malformed record on pipe, "
115434bb69d8SThomas Nikolajsen 				"illegal rec_size\n");
1155a7fbbf91SMatthew Dillon 			exit(1);
1156a7fbbf91SMatthew Dillon 		}
1157a7fbbf91SMatthew Dillon 
1158a7fbbf91SMatthew Dillon 		/*
1159a7fbbf91SMatthew Dillon 		 * Stop if we have insufficient space for the record and data.
1160a7fbbf91SMatthew Dillon 		 */
116117dd83bcSMatthew Dillon 		bytes = HAMMER_HEAD_DOALIGN(pickup->rec_size);
116217dd83bcSMatthew Dillon 		if (size - count < bytes)
1163a7fbbf91SMatthew Dillon 			break;
1164a7fbbf91SMatthew Dillon 
1165a7fbbf91SMatthew Dillon 		/*
116685a8e8a7SMatthew Dillon 		 * Stop if the record type is not a REC, SKIP, or PASS,
116785a8e8a7SMatthew Dillon 		 * which are the only types the ioctl supports.  Other types
116885a8e8a7SMatthew Dillon 		 * are used only by the userland protocol.
116985a8e8a7SMatthew Dillon 		 *
117085a8e8a7SMatthew Dillon 		 * Ignore all flags.
1171243ca327SMatthew Dillon 		 */
117285a8e8a7SMatthew Dillon 		type = pickup->type & HAMMER_MRECF_TYPE_LOMASK;
117385a8e8a7SMatthew Dillon 		if (type != HAMMER_MREC_TYPE_PFSD &&
117485a8e8a7SMatthew Dillon 		    type != HAMMER_MREC_TYPE_REC &&
117585a8e8a7SMatthew Dillon 		    type != HAMMER_MREC_TYPE_SKIP &&
117685a8e8a7SMatthew Dillon 		    type != HAMMER_MREC_TYPE_PASS) {
1177243ca327SMatthew Dillon 			break;
117817dd83bcSMatthew Dillon 		}
1179243ca327SMatthew Dillon 
1180243ca327SMatthew Dillon 		/*
1181a7fbbf91SMatthew Dillon 		 * Read the remainder and clear the pickup signature.
1182a7fbbf91SMatthew Dillon 		 */
118317dd83bcSMatthew Dillon 		for (n = HAMMER_MREC_HEADSIZE; n < bytes; n += i) {
118417dd83bcSMatthew Dillon 			i = read(fd, buf + count + n, bytes - n);
1185a7fbbf91SMatthew Dillon 			if (i <= 0)
1186a7fbbf91SMatthew Dillon 				break;
1187a7fbbf91SMatthew Dillon 		}
118817dd83bcSMatthew Dillon 		if (n != bytes) {
1189a7fbbf91SMatthew Dillon 			fprintf(stderr, "read_mrecords: short read on pipe\n");
1190a7fbbf91SMatthew Dillon 			exit(1);
1191a7fbbf91SMatthew Dillon 		}
119217dd83bcSMatthew Dillon 
119317dd83bcSMatthew Dillon 		bcopy(pickup, buf + count, HAMMER_MREC_HEADSIZE);
119417dd83bcSMatthew Dillon 		pickup->signature = 0;
119517dd83bcSMatthew Dillon 		pickup->type = 0;
119617dd83bcSMatthew Dillon 		mrec = (void *)(buf + count);
119717dd83bcSMatthew Dillon 
119817dd83bcSMatthew Dillon 		/*
119917dd83bcSMatthew Dillon 		 * Validate the completed record
120017dd83bcSMatthew Dillon 		 */
120117dd83bcSMatthew Dillon 		if (mrec->head.rec_crc !=
120217dd83bcSMatthew Dillon 		    crc32((char *)mrec + HAMMER_MREC_CRCOFF,
120317dd83bcSMatthew Dillon 			  mrec->head.rec_size - HAMMER_MREC_CRCOFF)) {
120417dd83bcSMatthew Dillon 			fprintf(stderr, "read_mrecords: malformed record "
120517dd83bcSMatthew Dillon 					"on pipe, bad crc\n");
120617dd83bcSMatthew Dillon 			exit(1);
1207a7fbbf91SMatthew Dillon 		}
1208a7fbbf91SMatthew Dillon 
120917dd83bcSMatthew Dillon 		/*
121085a8e8a7SMatthew Dillon 		 * If its a B-Tree record validate the data crc.
121185a8e8a7SMatthew Dillon 		 *
121285a8e8a7SMatthew Dillon 		 * NOTE: If the VFS passes us an explicitly errorde mrec
121385a8e8a7SMatthew Dillon 		 *	 we just pass it through.
121417dd83bcSMatthew Dillon 		 */
121585a8e8a7SMatthew Dillon 		type = mrec->head.type & HAMMER_MRECF_TYPE_MASK;
121685a8e8a7SMatthew Dillon 
121785a8e8a7SMatthew Dillon 		if (type == HAMMER_MREC_TYPE_REC) {
121817dd83bcSMatthew Dillon 			if (mrec->head.rec_size <
121917dd83bcSMatthew Dillon 			    sizeof(mrec->rec) + mrec->rec.leaf.data_len) {
122017dd83bcSMatthew Dillon 				fprintf(stderr,
122117dd83bcSMatthew Dillon 					"read_mrecords: malformed record on "
122217dd83bcSMatthew Dillon 					"pipe, illegal element data_len\n");
122317dd83bcSMatthew Dillon 				exit(1);
122417dd83bcSMatthew Dillon 			}
122517dd83bcSMatthew Dillon 			if (mrec->rec.leaf.data_len &&
122617dd83bcSMatthew Dillon 			    mrec->rec.leaf.data_offset &&
122717dd83bcSMatthew Dillon 			    hammer_crc_test_leaf(&mrec->rec + 1, &mrec->rec.leaf) == 0) {
122817dd83bcSMatthew Dillon 				fprintf(stderr,
122917dd83bcSMatthew Dillon 					"read_mrecords: data_crc did not "
1230a276dc6bSMatthew Dillon 					"match data! obj=%016jx key=%016jx\n",
1231a276dc6bSMatthew Dillon 					(uintmax_t)mrec->rec.leaf.base.obj_id,
1232a276dc6bSMatthew Dillon 					(uintmax_t)mrec->rec.leaf.base.key);
123317dd83bcSMatthew Dillon 				fprintf(stderr,
123417dd83bcSMatthew Dillon 					"continuing, but there are problems\n");
123517dd83bcSMatthew Dillon 			}
123617dd83bcSMatthew Dillon 		}
123717dd83bcSMatthew Dillon 		count += bytes;
1238a7fbbf91SMatthew Dillon 	}
1239a7fbbf91SMatthew Dillon 	return(count);
1240a7fbbf91SMatthew Dillon }
1241a7fbbf91SMatthew Dillon 
124234ebae70SMatthew Dillon /*
124317dd83bcSMatthew Dillon  * Read and return a single mrecord.
1244243ca327SMatthew Dillon  */
1245243ca327SMatthew Dillon static
124617dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t
124717dd83bcSMatthew Dillon read_mrecord(int fdin, int *errorp, hammer_ioc_mrecord_head_t pickup)
1248243ca327SMatthew Dillon {
124917dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
125017dd83bcSMatthew Dillon 	struct hammer_ioc_mrecord_head mrechd;
1251243ca327SMatthew Dillon 	size_t bytes;
1252243ca327SMatthew Dillon 	size_t n;
1253243ca327SMatthew Dillon 	size_t i;
1254243ca327SMatthew Dillon 
1255243ca327SMatthew Dillon 	if (pickup && pickup->type != 0) {
1256243ca327SMatthew Dillon 		mrechd = *pickup;
1257243ca327SMatthew Dillon 		pickup->signature = 0;
1258243ca327SMatthew Dillon 		pickup->type = 0;
1259243ca327SMatthew Dillon 		n = HAMMER_MREC_HEADSIZE;
1260243ca327SMatthew Dillon 	} else {
1261243ca327SMatthew Dillon 		/*
1262243ca327SMatthew Dillon 		 * Read in the PFSD header from the sender.
1263243ca327SMatthew Dillon 		 */
1264243ca327SMatthew Dillon 		for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) {
1265243ca327SMatthew Dillon 			i = read(fdin, (char *)&mrechd + n, HAMMER_MREC_HEADSIZE - n);
1266243ca327SMatthew Dillon 			if (i <= 0)
1267243ca327SMatthew Dillon 				break;
1268243ca327SMatthew Dillon 		}
1269243ca327SMatthew Dillon 		if (n == 0) {
1270243ca327SMatthew Dillon 			*errorp = 0;	/* EOF */
1271243ca327SMatthew Dillon 			return(NULL);
1272243ca327SMatthew Dillon 		}
1273243ca327SMatthew Dillon 		if (n != HAMMER_MREC_HEADSIZE) {
1274243ca327SMatthew Dillon 			fprintf(stderr, "short read of mrecord header\n");
1275243ca327SMatthew Dillon 			*errorp = EPIPE;
1276243ca327SMatthew Dillon 			return(NULL);
1277243ca327SMatthew Dillon 		}
1278243ca327SMatthew Dillon 	}
1279243ca327SMatthew Dillon 	if (mrechd.signature != HAMMER_IOC_MIRROR_SIGNATURE) {
1280243ca327SMatthew Dillon 		fprintf(stderr, "read_mrecord: bad signature\n");
1281243ca327SMatthew Dillon 		*errorp = EINVAL;
1282243ca327SMatthew Dillon 		return(NULL);
1283243ca327SMatthew Dillon 	}
128417dd83bcSMatthew Dillon 	bytes = HAMMER_HEAD_DOALIGN(mrechd.rec_size);
128517dd83bcSMatthew Dillon 	assert(bytes >= sizeof(mrechd));
1286243ca327SMatthew Dillon 	mrec = malloc(bytes);
128717dd83bcSMatthew Dillon 	mrec->head = mrechd;
128817dd83bcSMatthew Dillon 
1289243ca327SMatthew Dillon 	while (n < bytes) {
1290243ca327SMatthew Dillon 		i = read(fdin, (char *)mrec + n, bytes - n);
1291243ca327SMatthew Dillon 		if (i <= 0)
1292243ca327SMatthew Dillon 			break;
1293243ca327SMatthew Dillon 		n += i;
1294243ca327SMatthew Dillon 	}
1295243ca327SMatthew Dillon 	if (n != bytes) {
1296243ca327SMatthew Dillon 		fprintf(stderr, "read_mrecord: short read on payload\n");
1297243ca327SMatthew Dillon 		*errorp = EPIPE;
1298243ca327SMatthew Dillon 		return(NULL);
1299243ca327SMatthew Dillon 	}
130017dd83bcSMatthew Dillon 	if (mrec->head.rec_crc !=
130117dd83bcSMatthew Dillon 	    crc32((char *)mrec + HAMMER_MREC_CRCOFF,
130217dd83bcSMatthew Dillon 		  mrec->head.rec_size - HAMMER_MREC_CRCOFF)) {
1303243ca327SMatthew Dillon 		fprintf(stderr, "read_mrecord: bad CRC\n");
1304243ca327SMatthew Dillon 		*errorp = EINVAL;
1305243ca327SMatthew Dillon 		return(NULL);
1306243ca327SMatthew Dillon 	}
1307243ca327SMatthew Dillon 	*errorp = 0;
1308243ca327SMatthew Dillon 	return(mrec);
1309243ca327SMatthew Dillon }
1310243ca327SMatthew Dillon 
1311243ca327SMatthew Dillon static
1312243ca327SMatthew Dillon void
131317dd83bcSMatthew Dillon write_mrecord(int fdout, u_int32_t type, hammer_ioc_mrecord_any_t mrec,
131417dd83bcSMatthew Dillon 	      int bytes)
1315243ca327SMatthew Dillon {
131617dd83bcSMatthew Dillon 	char zbuf[HAMMER_HEAD_ALIGN];
131717dd83bcSMatthew Dillon 	int pad;
1318243ca327SMatthew Dillon 
131917dd83bcSMatthew Dillon 	pad = HAMMER_HEAD_DOALIGN(bytes) - bytes;
132017dd83bcSMatthew Dillon 
132117dd83bcSMatthew Dillon 	assert(bytes >= (int)sizeof(mrec->head));
132217dd83bcSMatthew Dillon 	bzero(&mrec->head, sizeof(mrec->head));
132317dd83bcSMatthew Dillon 	mrec->head.signature = HAMMER_IOC_MIRROR_SIGNATURE;
132417dd83bcSMatthew Dillon 	mrec->head.type = type;
132517dd83bcSMatthew Dillon 	mrec->head.rec_size = bytes;
132617dd83bcSMatthew Dillon 	mrec->head.rec_crc = crc32((char *)mrec + HAMMER_MREC_CRCOFF,
132717dd83bcSMatthew Dillon 				   bytes - HAMMER_MREC_CRCOFF);
132817dd83bcSMatthew Dillon 	if (write(fdout, mrec, bytes) != bytes) {
1329243ca327SMatthew Dillon 		fprintf(stderr, "write_mrecord: error %d (%s)\n",
1330243ca327SMatthew Dillon 			errno, strerror(errno));
1331243ca327SMatthew Dillon 		exit(1);
1332243ca327SMatthew Dillon 	}
133317dd83bcSMatthew Dillon 	if (pad) {
133417dd83bcSMatthew Dillon 		bzero(zbuf, pad);
133517dd83bcSMatthew Dillon 		if (write(fdout, zbuf, pad) != pad) {
133617dd83bcSMatthew Dillon 			fprintf(stderr, "write_mrecord: error %d (%s)\n",
133717dd83bcSMatthew Dillon 				errno, strerror(errno));
133817dd83bcSMatthew Dillon 			exit(1);
133917dd83bcSMatthew Dillon 		}
134017dd83bcSMatthew Dillon 	}
1341243ca327SMatthew Dillon }
1342243ca327SMatthew Dillon 
1343243ca327SMatthew Dillon /*
134434ebae70SMatthew Dillon  * Generate a mirroring header with the pfs information of the
134534ebae70SMatthew Dillon  * originating filesytem.
134634ebae70SMatthew Dillon  */
134734ebae70SMatthew Dillon static void
1348e7f926a5SMatthew Dillon generate_mrec_header(int fd, int pfs_id,
1349e7f926a5SMatthew Dillon 		     union hammer_ioc_mrecord_any *mrec_tmp)
135034ebae70SMatthew Dillon {
135134ebae70SMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
135234ebae70SMatthew Dillon 
135334ebae70SMatthew Dillon 	bzero(&pfs, sizeof(pfs));
1354e7f926a5SMatthew Dillon 	bzero(mrec_tmp, sizeof(*mrec_tmp));
1355d4e5b69bSMatthew Dillon 	pfs.pfs_id = pfs_id;
1356e7f926a5SMatthew Dillon 	pfs.ondisk = &mrec_tmp->pfs.pfsd;
1357e7f926a5SMatthew Dillon 	pfs.bytes = sizeof(mrec_tmp->pfs.pfsd);
135834ebae70SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
13599dc76cb1SMatthew Dillon 		fprintf(stderr, "Mirror-read: not a HAMMER fs/pseudofs!\n");
136034ebae70SMatthew Dillon 		exit(1);
136134ebae70SMatthew Dillon 	}
136234ebae70SMatthew Dillon 	if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
13639dc76cb1SMatthew Dillon 		fprintf(stderr, "Mirror-read: HAMMER pfs version mismatch!\n");
136434ebae70SMatthew Dillon 		exit(1);
136534ebae70SMatthew Dillon 	}
1366e7f926a5SMatthew Dillon 	mrec_tmp->pfs.version = pfs.version;
136734ebae70SMatthew Dillon }
136834ebae70SMatthew Dillon 
136934ebae70SMatthew Dillon /*
137034ebae70SMatthew Dillon  * Validate the pfs information from the originating filesystem
137134ebae70SMatthew Dillon  * against the target filesystem.  shared_uuid must match.
137248eadef9SMatthew Dillon  *
137348eadef9SMatthew Dillon  * return -1 if we got a TERM record
137434ebae70SMatthew Dillon  */
137548eadef9SMatthew Dillon static int
1376d4e5b69bSMatthew Dillon validate_mrec_header(int fd, int fdin, int is_target, int pfs_id,
137748eadef9SMatthew Dillon 		     struct hammer_ioc_mrecord_head *pickup,
137834ebae70SMatthew Dillon 		     hammer_tid_t *tid_begp, hammer_tid_t *tid_endp)
137934ebae70SMatthew Dillon {
138034ebae70SMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
138134ebae70SMatthew Dillon 	struct hammer_pseudofs_data pfsd;
138217dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
1383243ca327SMatthew Dillon 	int error;
138434ebae70SMatthew Dillon 
138534ebae70SMatthew Dillon 	/*
138634ebae70SMatthew Dillon 	 * Get the PFSD info from the target filesystem.
138734ebae70SMatthew Dillon 	 */
138834ebae70SMatthew Dillon 	bzero(&pfs, sizeof(pfs));
138934ebae70SMatthew Dillon 	bzero(&pfsd, sizeof(pfsd));
1390d4e5b69bSMatthew Dillon 	pfs.pfs_id = pfs_id;
139134ebae70SMatthew Dillon 	pfs.ondisk = &pfsd;
139234ebae70SMatthew Dillon 	pfs.bytes = sizeof(pfsd);
139334ebae70SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
139434ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: not a HAMMER fs/pseudofs!\n");
139534ebae70SMatthew Dillon 		exit(1);
139634ebae70SMatthew Dillon 	}
139734ebae70SMatthew Dillon 	if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
139834ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: HAMMER pfs version mismatch!\n");
139934ebae70SMatthew Dillon 		exit(1);
140034ebae70SMatthew Dillon 	}
140134ebae70SMatthew Dillon 
140248eadef9SMatthew Dillon 	mrec = read_mrecord(fdin, &error, pickup);
1403243ca327SMatthew Dillon 	if (mrec == NULL) {
1404243ca327SMatthew Dillon 		if (error == 0)
1405243ca327SMatthew Dillon 			fprintf(stderr, "validate_mrec_header: short read\n");
140634ebae70SMatthew Dillon 		exit(1);
140734ebae70SMatthew Dillon 	}
140848eadef9SMatthew Dillon 	if (mrec->head.type == HAMMER_MREC_TYPE_TERM) {
140948eadef9SMatthew Dillon 		free(mrec);
141048eadef9SMatthew Dillon 		return(-1);
141148eadef9SMatthew Dillon 	}
141248eadef9SMatthew Dillon 
141317dd83bcSMatthew Dillon 	if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) {
1414243ca327SMatthew Dillon 		fprintf(stderr, "validate_mrec_header: did not get expected "
1415243ca327SMatthew Dillon 				"PFSD record type\n");
141634ebae70SMatthew Dillon 		exit(1);
141734ebae70SMatthew Dillon 	}
141817dd83bcSMatthew Dillon 	if (mrec->head.rec_size != sizeof(mrec->pfs)) {
1419243ca327SMatthew Dillon 		fprintf(stderr, "validate_mrec_header: unexpected payload "
1420243ca327SMatthew Dillon 				"size\n");
142134ebae70SMatthew Dillon 		exit(1);
142234ebae70SMatthew Dillon 	}
142317dd83bcSMatthew Dillon 	if (mrec->pfs.version != pfs.version) {
1424243ca327SMatthew Dillon 		fprintf(stderr, "validate_mrec_header: Version mismatch\n");
142534ebae70SMatthew Dillon 		exit(1);
142634ebae70SMatthew Dillon 	}
142734ebae70SMatthew Dillon 
142834ebae70SMatthew Dillon 	/*
142934ebae70SMatthew Dillon 	 * Whew.  Ok, is the read PFS info compatible with the target?
143034ebae70SMatthew Dillon 	 */
143117dd83bcSMatthew Dillon 	if (bcmp(&mrec->pfs.pfsd.shared_uuid, &pfsd.shared_uuid,
143217dd83bcSMatthew Dillon 		 sizeof(pfsd.shared_uuid)) != 0) {
143317dd83bcSMatthew Dillon 		fprintf(stderr,
143417dd83bcSMatthew Dillon 			"mirror-write: source and target have "
1435f265b84fSSascha Wildner 			"different shared-uuid's!\n");
143634ebae70SMatthew Dillon 		exit(1);
143734ebae70SMatthew Dillon 	}
1438d4e5b69bSMatthew Dillon 	if (is_target &&
1439d4e5b69bSMatthew Dillon 	    (pfsd.mirror_flags & HAMMER_PFSD_SLAVE) == 0) {
144034ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: target must be in slave mode\n");
144134ebae70SMatthew Dillon 		exit(1);
144234ebae70SMatthew Dillon 	}
1443d4e5b69bSMatthew Dillon 	if (tid_begp)
144417dd83bcSMatthew Dillon 		*tid_begp = mrec->pfs.pfsd.sync_beg_tid;
1445d4e5b69bSMatthew Dillon 	if (tid_endp)
144617dd83bcSMatthew Dillon 		*tid_endp = mrec->pfs.pfsd.sync_end_tid;
1447243ca327SMatthew Dillon 	free(mrec);
144848eadef9SMatthew Dillon 	return(0);
144934ebae70SMatthew Dillon }
145034ebae70SMatthew Dillon 
145134ebae70SMatthew Dillon static void
1452d4e5b69bSMatthew Dillon update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id)
145334ebae70SMatthew Dillon {
1454243ca327SMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
1455243ca327SMatthew Dillon 	struct hammer_pseudofs_data pfsd;
145634ebae70SMatthew Dillon 
1457243ca327SMatthew Dillon 	bzero(&pfs, sizeof(pfs));
1458243ca327SMatthew Dillon 	bzero(&pfsd, sizeof(pfsd));
1459d4e5b69bSMatthew Dillon 	pfs.pfs_id = pfs_id;
1460243ca327SMatthew Dillon 	pfs.ondisk = &pfsd;
1461243ca327SMatthew Dillon 	pfs.bytes = sizeof(pfsd);
1462243ca327SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
1463243ca327SMatthew Dillon 		perror("update_pfs_snapshot (read)");
1464243ca327SMatthew Dillon 		exit(1);
146534ebae70SMatthew Dillon 	}
14669c67b4d2SMatthew Dillon 	if (pfsd.sync_end_tid != snapshot_tid) {
1467ddc8e722SMatthew Dillon 		pfsd.sync_end_tid = snapshot_tid;
1468243ca327SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) != 0) {
1469243ca327SMatthew Dillon 			perror("update_pfs_snapshot (rewrite)");
1470243ca327SMatthew Dillon 			exit(1);
147134ebae70SMatthew Dillon 		}
147248eadef9SMatthew Dillon 		if (VerboseOpt >= 2) {
14739dc76cb1SMatthew Dillon 			fprintf(stderr,
1474*ced4470dSMatthew Dillon 				"Mirror-write: Completed, updated snapshot "
1475a276dc6bSMatthew Dillon 				"to %016jx\n",
1476a276dc6bSMatthew Dillon 				(uintmax_t)snapshot_tid);
1477*ced4470dSMatthew Dillon 			fflush(stderr);
1478243ca327SMatthew Dillon 		}
14799c67b4d2SMatthew Dillon 	}
148048eadef9SMatthew Dillon }
1481243ca327SMatthew Dillon 
148248eadef9SMatthew Dillon /*
148348eadef9SMatthew Dillon  * Bandwidth-limited write in chunks
148448eadef9SMatthew Dillon  */
148548eadef9SMatthew Dillon static
148648eadef9SMatthew Dillon ssize_t
148748eadef9SMatthew Dillon writebw(int fd, const void *buf, size_t nbytes,
148848eadef9SMatthew Dillon 	u_int64_t *bwcount, struct timeval *tv1)
148948eadef9SMatthew Dillon {
149048eadef9SMatthew Dillon 	struct timeval tv2;
149148eadef9SMatthew Dillon 	size_t n;
149248eadef9SMatthew Dillon 	ssize_t r;
149348eadef9SMatthew Dillon 	ssize_t a;
149448eadef9SMatthew Dillon 	int usec;
149548eadef9SMatthew Dillon 
149648eadef9SMatthew Dillon 	a = 0;
149748eadef9SMatthew Dillon 	r = 0;
149848eadef9SMatthew Dillon 	while (nbytes) {
149948eadef9SMatthew Dillon 		if (*bwcount + nbytes > BandwidthOpt)
150048eadef9SMatthew Dillon 			n = BandwidthOpt - *bwcount;
150148eadef9SMatthew Dillon 		else
150248eadef9SMatthew Dillon 			n = nbytes;
150348eadef9SMatthew Dillon 		if (n)
150448eadef9SMatthew Dillon 			r = write(fd, buf, n);
150548eadef9SMatthew Dillon 		if (r >= 0) {
150648eadef9SMatthew Dillon 			a += r;
150748eadef9SMatthew Dillon 			nbytes -= r;
150848eadef9SMatthew Dillon 			buf = (const char *)buf + r;
150948eadef9SMatthew Dillon 		}
151048eadef9SMatthew Dillon 		if ((size_t)r != n)
151148eadef9SMatthew Dillon 			break;
151248eadef9SMatthew Dillon 		*bwcount += n;
151348eadef9SMatthew Dillon 		if (*bwcount >= BandwidthOpt) {
151448eadef9SMatthew Dillon 			gettimeofday(&tv2, NULL);
151548eadef9SMatthew Dillon 			usec = (int)(tv2.tv_sec - tv1->tv_sec) * 1000000 +
151648eadef9SMatthew Dillon 				(int)(tv2.tv_usec - tv1->tv_usec);
151748eadef9SMatthew Dillon 			if (usec >= 0 && usec < 1000000)
151848eadef9SMatthew Dillon 				usleep(1000000 - usec);
151948eadef9SMatthew Dillon 			gettimeofday(tv1, NULL);
152048eadef9SMatthew Dillon 			*bwcount -= BandwidthOpt;
152148eadef9SMatthew Dillon 		}
152248eadef9SMatthew Dillon 	}
152348eadef9SMatthew Dillon 	return(a ? a : r);
152448eadef9SMatthew Dillon }
152534ebae70SMatthew Dillon 
152601a72c9fSMatthew Dillon /*
152701a72c9fSMatthew Dillon  * Get a yes or no answer from the terminal.  The program may be run as
152801a72c9fSMatthew Dillon  * part of a two-way pipe so we cannot use stdin for this operation.
152901a72c9fSMatthew Dillon  */
153001a72c9fSMatthew Dillon static int
153101a72c9fSMatthew Dillon getyn(void)
153201a72c9fSMatthew Dillon {
153301a72c9fSMatthew Dillon 	char buf[256];
153401a72c9fSMatthew Dillon 	FILE *fp;
153501a72c9fSMatthew Dillon 	int result;
153601a72c9fSMatthew Dillon 
153701a72c9fSMatthew Dillon 	fp = fopen("/dev/tty", "r");
153801a72c9fSMatthew Dillon 	if (fp == NULL) {
153901a72c9fSMatthew Dillon 		fprintf(stderr, "No terminal for response\n");
154001a72c9fSMatthew Dillon 		return(-1);
154101a72c9fSMatthew Dillon 	}
154201a72c9fSMatthew Dillon 	result = -1;
154301a72c9fSMatthew Dillon 	while (fgets(buf, sizeof(buf), fp) != NULL) {
154401a72c9fSMatthew Dillon 		if (buf[0] == 'y' || buf[0] == 'Y') {
154501a72c9fSMatthew Dillon 			result = 1;
154601a72c9fSMatthew Dillon 			break;
154701a72c9fSMatthew Dillon 		}
154801a72c9fSMatthew Dillon 		if (buf[0] == 'n' || buf[0] == 'N') {
154901a72c9fSMatthew Dillon 			result = 0;
155001a72c9fSMatthew Dillon 			break;
155101a72c9fSMatthew Dillon 		}
155201a72c9fSMatthew Dillon 		fprintf(stderr, "Response not understood\n");
155301a72c9fSMatthew Dillon 		break;
155401a72c9fSMatthew Dillon 	}
155501a72c9fSMatthew Dillon 	fclose(fp);
155601a72c9fSMatthew Dillon 	return(result);
155701a72c9fSMatthew Dillon }
155801a72c9fSMatthew Dillon 
1559a7fbbf91SMatthew Dillon static void
1560a7fbbf91SMatthew Dillon mirror_usage(int code)
1561a7fbbf91SMatthew Dillon {
1562a7fbbf91SMatthew Dillon 	fprintf(stderr,
156334bb69d8SThomas Nikolajsen 		"hammer mirror-read <filesystem> [begin-tid]\n"
156434bb69d8SThomas Nikolajsen 		"hammer mirror-read-stream <filesystem> [begin-tid]\n"
1565a7fbbf91SMatthew Dillon 		"hammer mirror-write <filesystem>\n"
1566243ca327SMatthew Dillon 		"hammer mirror-dump\n"
156734bb69d8SThomas Nikolajsen 		"hammer mirror-copy [[user@]host:]<filesystem>"
156834bb69d8SThomas Nikolajsen 				  " [[user@]host:]<filesystem>\n"
156934bb69d8SThomas Nikolajsen 		"hammer mirror-stream [[user@]host:]<filesystem>"
157034bb69d8SThomas Nikolajsen 				    " [[user@]host:]<filesystem>\n"
1571a7fbbf91SMatthew Dillon 	);
1572a7fbbf91SMatthew Dillon 	exit(code);
1573a7fbbf91SMatthew Dillon }
157401a72c9fSMatthew Dillon 
1575