xref: /dflybsd-src/sbin/hammer/cmd_mirror.c (revision 0bd7a37c6472ea619a1815995fa681750bb378cb)
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 
41a7fbbf91SMatthew Dillon static int read_mrecords(int fd, char *buf, u_int size,
4217dd83bcSMatthew Dillon 			 hammer_ioc_mrecord_head_t pickup);
43e7f926a5SMatthew Dillon static int generate_histogram(int fd, const char *filesystem,
44e7f926a5SMatthew Dillon 			 hammer_tid_t **histogram_ary,
45e7f926a5SMatthew Dillon 			 struct hammer_ioc_mirror_rw *mirror_base);
4617dd83bcSMatthew Dillon static hammer_ioc_mrecord_any_t read_mrecord(int fdin, int *errorp,
4717dd83bcSMatthew Dillon 			 hammer_ioc_mrecord_head_t pickup);
4817dd83bcSMatthew Dillon static void write_mrecord(int fdout, u_int32_t type,
4917dd83bcSMatthew Dillon 			 hammer_ioc_mrecord_any_t mrec, int bytes);
50e7f926a5SMatthew Dillon static void generate_mrec_header(int fd, int pfs_id,
51e7f926a5SMatthew Dillon 			 union hammer_ioc_mrecord_any *mrec_tmp);
5248eadef9SMatthew Dillon static int validate_mrec_header(int fd, int fdin, int is_target, int pfs_id,
5348eadef9SMatthew Dillon 			 struct hammer_ioc_mrecord_head *pickup,
5434ebae70SMatthew Dillon 			 hammer_tid_t *tid_begp, hammer_tid_t *tid_endp);
55d4e5b69bSMatthew Dillon static void update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id);
5648eadef9SMatthew Dillon static ssize_t writebw(int fd, const void *buf, size_t nbytes,
5748eadef9SMatthew Dillon 			u_int64_t *bwcount, struct timeval *tv1);
5801a72c9fSMatthew Dillon static int getyn(void);
59a7fbbf91SMatthew Dillon static void mirror_usage(int code);
60a7fbbf91SMatthew Dillon 
61e7f926a5SMatthew Dillon #define BULK_MINIMUM	20000
62e7f926a5SMatthew Dillon 
6317dd83bcSMatthew Dillon /*
6417dd83bcSMatthew Dillon  * Generate a mirroring data stream from the specific source over the
6517dd83bcSMatthew Dillon  * entire key range, but restricted to the specified transaction range.
6617dd83bcSMatthew Dillon  *
6717dd83bcSMatthew Dillon  * The HAMMER VFS does most of the work, we add a few new mrecord
6817dd83bcSMatthew Dillon  * types to negotiate the TID ranges and verify that the entire
6917dd83bcSMatthew Dillon  * stream made it to the destination.
7017dd83bcSMatthew Dillon  */
71a7fbbf91SMatthew Dillon void
7248eadef9SMatthew Dillon hammer_cmd_mirror_read(char **av, int ac, int streaming)
73a7fbbf91SMatthew Dillon {
74a7fbbf91SMatthew Dillon 	struct hammer_ioc_mirror_rw mirror;
75d4e5b69bSMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
7617dd83bcSMatthew Dillon 	union hammer_ioc_mrecord_any mrec_tmp;
7748eadef9SMatthew Dillon 	struct hammer_ioc_mrecord_head pickup;
7817dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
79243ca327SMatthew Dillon 	hammer_tid_t sync_tid;
80e7f926a5SMatthew Dillon 	hammer_tid_t *histogram_ary;
81a7fbbf91SMatthew Dillon 	const char *filesystem;
82a7fbbf91SMatthew Dillon 	char *buf = malloc(SERIALBUF_SIZE);
83243ca327SMatthew Dillon 	int interrupted = 0;
84243ca327SMatthew Dillon 	int error;
85a7fbbf91SMatthew Dillon 	int fd;
86243ca327SMatthew Dillon 	int n;
8748eadef9SMatthew Dillon 	int didwork;
88e7f926a5SMatthew Dillon 	int histogram;
8948eadef9SMatthew Dillon 	int64_t total_bytes;
90243ca327SMatthew Dillon 	time_t base_t = time(NULL);
9148eadef9SMatthew Dillon 	struct timeval bwtv;
9248eadef9SMatthew Dillon 	u_int64_t bwcount;
93a7fbbf91SMatthew Dillon 
9434bb69d8SThomas Nikolajsen 	if (ac == 0 || ac > 2)
95a7fbbf91SMatthew Dillon 		mirror_usage(1);
96a7fbbf91SMatthew Dillon 	filesystem = av[0];
97a7fbbf91SMatthew Dillon 
9848eadef9SMatthew Dillon 	pickup.signature = 0;
9948eadef9SMatthew Dillon 	pickup.type = 0;
100e7f926a5SMatthew Dillon 	histogram = -1;
101e7f926a5SMatthew Dillon 	histogram_ary = NULL;
10248eadef9SMatthew Dillon 
10348eadef9SMatthew Dillon again:
104a7fbbf91SMatthew Dillon 	bzero(&mirror, sizeof(mirror));
105a7fbbf91SMatthew Dillon 	hammer_key_beg_init(&mirror.key_beg);
106a7fbbf91SMatthew Dillon 	hammer_key_end_init(&mirror.key_end);
107a7fbbf91SMatthew Dillon 
108d4e5b69bSMatthew Dillon 	fd = getpfs(&pfs, filesystem);
109a7fbbf91SMatthew Dillon 
11048eadef9SMatthew Dillon 	if (streaming && VerboseOpt) {
11148eadef9SMatthew Dillon 		fprintf(stderr, "\nRunning");
11248eadef9SMatthew Dillon 		fflush(stderr);
11348eadef9SMatthew Dillon 	}
11448eadef9SMatthew Dillon 	total_bytes = 0;
11548eadef9SMatthew Dillon 	gettimeofday(&bwtv, NULL);
11648eadef9SMatthew Dillon 	bwcount = 0;
11748eadef9SMatthew Dillon 
118243ca327SMatthew Dillon 	/*
119e7f926a5SMatthew Dillon 	 * Send initial header for the purpose of determining the
120e7f926a5SMatthew Dillon 	 * shared-uuid.
12101a72c9fSMatthew Dillon 	 */
122e7f926a5SMatthew Dillon 	generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp);
123e7f926a5SMatthew Dillon 	write_mrecord(1, HAMMER_MREC_TYPE_PFSD,
124e7f926a5SMatthew Dillon 		      &mrec_tmp, sizeof(mrec_tmp.pfs));
12501a72c9fSMatthew Dillon 
12601a72c9fSMatthew Dillon 	/*
127d4e5b69bSMatthew Dillon 	 * In 2-way mode the target will send us a PFS info packet
128d4e5b69bSMatthew Dillon 	 * first.  Use the target's current snapshot TID as our default
129d4e5b69bSMatthew Dillon 	 * begin TID.
130243ca327SMatthew Dillon 	 */
131d4e5b69bSMatthew Dillon 	mirror.tid_beg = 0;
13248eadef9SMatthew Dillon 	if (TwoWayPipeOpt) {
13348eadef9SMatthew Dillon 		n = validate_mrec_header(fd, 0, 0, pfs.pfs_id, &pickup,
134d4e5b69bSMatthew Dillon 					 NULL, &mirror.tid_beg);
13548eadef9SMatthew Dillon 		if (n < 0) {	/* got TERM record */
13648eadef9SMatthew Dillon 			relpfs(fd, &pfs);
13748eadef9SMatthew Dillon 			return;
13848eadef9SMatthew Dillon 		}
13948eadef9SMatthew Dillon 		++mirror.tid_beg;
14048eadef9SMatthew Dillon 	}
141d4e5b69bSMatthew Dillon 
142d4e5b69bSMatthew Dillon 	/*
143d4e5b69bSMatthew Dillon 	 * Write out the PFS header, tid_beg will be updated if our PFS
144d4e5b69bSMatthew Dillon 	 * has a larger begin sync.  tid_end is set to the latest source
145d4e5b69bSMatthew Dillon 	 * TID whos flush cycle has completed.
146d4e5b69bSMatthew Dillon 	 */
147e7f926a5SMatthew Dillon 	generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp);
148e7f926a5SMatthew Dillon 	if (mirror.tid_beg < mrec_tmp.pfs.pfsd.sync_beg_tid)
149e7f926a5SMatthew Dillon 		mirror.tid_beg = mrec_tmp.pfs.pfsd.sync_beg_tid;
150e7f926a5SMatthew Dillon 	mirror.tid_end = mrec_tmp.pfs.pfsd.sync_end_tid;
151e7f926a5SMatthew Dillon 	mirror.ubuf = buf;
152e7f926a5SMatthew Dillon 	mirror.size = SERIALBUF_SIZE;
153e7f926a5SMatthew Dillon 	mirror.pfs_id = pfs.pfs_id;
154e7f926a5SMatthew Dillon 	mirror.shared_uuid = pfs.ondisk->shared_uuid;
155d4e5b69bSMatthew Dillon 
156d4e5b69bSMatthew Dillon 	/*
157e7f926a5SMatthew Dillon 	 * XXX If the histogram is exhausted and the TID delta is large
158e7f926a5SMatthew Dillon 	 *     the stream might have been offline for a while and is
159e7f926a5SMatthew Dillon 	 *     now picking it up again.  Do another histogram.
160d4e5b69bSMatthew Dillon 	 */
161e7f926a5SMatthew Dillon #if 0
162e7f926a5SMatthew Dillon 	if (TwoWayPipeOpt && streaming && histogram == 0) {
163e7f926a5SMatthew Dillon 		if (mirror.tid_end - mirror.tid_beg > BULK_MINIMUM)
164e7f926a5SMatthew Dillon 			histogram = -1;
165e7f926a5SMatthew Dillon 	}
166e7f926a5SMatthew Dillon #endif
16734ebae70SMatthew Dillon 
168e7f926a5SMatthew Dillon 	/*
169e7f926a5SMatthew Dillon 	 * Initial bulk startup control, try to do some incremental
170e7f926a5SMatthew Dillon 	 * mirroring in order to allow the stream to be killed and
171e7f926a5SMatthew Dillon 	 * restarted without having to start over.
172e7f926a5SMatthew Dillon 	 */
173*0bd7a37cSMatthew Dillon 	if (histogram < 0 && BulkOpt == 0) {
174e7f926a5SMatthew Dillon 		if (VerboseOpt)
175e7f926a5SMatthew Dillon 			fprintf(stderr, "\n");
176e7f926a5SMatthew Dillon 		histogram = generate_histogram(fd, filesystem,
177e7f926a5SMatthew Dillon 					       &histogram_ary, &mirror);
178e7f926a5SMatthew Dillon 	}
179e7f926a5SMatthew Dillon 
180e7f926a5SMatthew Dillon 	if (TwoWayPipeOpt && streaming && histogram > 0) {
181e7f926a5SMatthew Dillon 		mirror.tid_end = histogram_ary[--histogram];
182e7f926a5SMatthew Dillon 		mrec_tmp.pfs.pfsd.sync_end_tid = mirror.tid_end;
183e7f926a5SMatthew Dillon 	}
184e7f926a5SMatthew Dillon 
185e7f926a5SMatthew Dillon 	write_mrecord(1, HAMMER_MREC_TYPE_PFSD,
186e7f926a5SMatthew Dillon 		      &mrec_tmp, sizeof(mrec_tmp.pfs));
187e7f926a5SMatthew Dillon 
188e7f926a5SMatthew Dillon 	/*
189e7f926a5SMatthew Dillon 	 * A cycle file overrides the beginning TID only if we are
190e7f926a5SMatthew Dillon 	 * not operating in two-way mode.
191e7f926a5SMatthew Dillon 	 */
192e7f926a5SMatthew Dillon 	if (TwoWayPipeOpt == 0) {
193e7f926a5SMatthew Dillon 		hammer_get_cycle(&mirror.key_beg, &mirror.tid_beg);
194e7f926a5SMatthew Dillon 	}
195e7f926a5SMatthew Dillon 
196e7f926a5SMatthew Dillon 	/*
197e7f926a5SMatthew Dillon 	 * An additional argument overrides the beginning TID regardless
198e7f926a5SMatthew Dillon 	 * of what mode we are in.  This is not recommending if operating
199e7f926a5SMatthew Dillon 	 * in two-way mode.
200e7f926a5SMatthew Dillon 	 */
20117dd83bcSMatthew Dillon 	if (ac == 2)
20217dd83bcSMatthew Dillon 		mirror.tid_beg = strtoull(av[1], NULL, 0);
20317dd83bcSMatthew Dillon 
20448eadef9SMatthew Dillon 	if (streaming == 0 || VerboseOpt >= 2) {
20548eadef9SMatthew Dillon 		fprintf(stderr,
20648eadef9SMatthew Dillon 			"Mirror-read: Mirror from %016llx to %016llx\n",
207243ca327SMatthew Dillon 			mirror.tid_beg, mirror.tid_end);
20848eadef9SMatthew Dillon 	}
209243ca327SMatthew Dillon 	if (mirror.key_beg.obj_id != (int64_t)HAMMER_MIN_OBJID) {
2109dc76cb1SMatthew Dillon 		fprintf(stderr, "Mirror-read: Resuming at object %016llx\n",
211243ca327SMatthew Dillon 			mirror.key_beg.obj_id);
212243ca327SMatthew Dillon 	}
213243ca327SMatthew Dillon 
214243ca327SMatthew Dillon 	/*
2159dc76cb1SMatthew Dillon 	 * Nothing to do if begin equals end.
2169dc76cb1SMatthew Dillon 	 */
21748eadef9SMatthew Dillon 	if (mirror.tid_beg >= mirror.tid_end) {
21848eadef9SMatthew Dillon 		if (streaming == 0 || VerboseOpt >= 2)
21948eadef9SMatthew Dillon 			fprintf(stderr, "Mirror-read: No work to do\n");
22048eadef9SMatthew Dillon 		didwork = 0;
2219dc76cb1SMatthew Dillon 		goto done;
2229dc76cb1SMatthew Dillon 	}
22348eadef9SMatthew Dillon 	didwork = 1;
2249dc76cb1SMatthew Dillon 
2259dc76cb1SMatthew Dillon 	/*
226243ca327SMatthew Dillon 	 * Write out bulk records
227243ca327SMatthew Dillon 	 */
228a7fbbf91SMatthew Dillon 	mirror.ubuf = buf;
229a7fbbf91SMatthew Dillon 	mirror.size = SERIALBUF_SIZE;
230a7fbbf91SMatthew Dillon 
231a7fbbf91SMatthew Dillon 	do {
232a7fbbf91SMatthew Dillon 		mirror.count = 0;
233d4e5b69bSMatthew Dillon 		mirror.pfs_id = pfs.pfs_id;
234d4e5b69bSMatthew Dillon 		mirror.shared_uuid = pfs.ondisk->shared_uuid;
235a7fbbf91SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) {
236a7fbbf91SMatthew Dillon 			fprintf(stderr, "Mirror-read %s failed: %s\n",
237a7fbbf91SMatthew Dillon 				filesystem, strerror(errno));
238a7fbbf91SMatthew Dillon 			exit(1);
239a7fbbf91SMatthew Dillon 		}
2409c67b4d2SMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) {
2419c67b4d2SMatthew Dillon 			fprintf(stderr,
2429c67b4d2SMatthew Dillon 				"Mirror-read %s fatal error %d\n",
2439c67b4d2SMatthew Dillon 				filesystem, mirror.head.error);
2449c67b4d2SMatthew Dillon 			exit(1);
2459c67b4d2SMatthew Dillon 		}
246243ca327SMatthew Dillon 		if (mirror.count) {
24748eadef9SMatthew Dillon 			if (BandwidthOpt) {
24848eadef9SMatthew Dillon 				n = writebw(1, mirror.ubuf, mirror.count,
24948eadef9SMatthew Dillon 					    &bwcount, &bwtv);
25048eadef9SMatthew Dillon 			} else {
251243ca327SMatthew Dillon 				n = write(1, mirror.ubuf, mirror.count);
25248eadef9SMatthew Dillon 			}
253243ca327SMatthew Dillon 			if (n != mirror.count) {
254243ca327SMatthew Dillon 				fprintf(stderr, "Mirror-read %s failed: "
255243ca327SMatthew Dillon 						"short write\n",
256243ca327SMatthew Dillon 				filesystem);
257243ca327SMatthew Dillon 				exit(1);
258243ca327SMatthew Dillon 			}
259a7fbbf91SMatthew Dillon 		}
26048eadef9SMatthew Dillon 		total_bytes += mirror.count;
26148eadef9SMatthew Dillon 		if (streaming && VerboseOpt) {
262e7f926a5SMatthew Dillon 			fprintf(stderr,
263e7f926a5SMatthew Dillon 				"\robj=%016llx tids=%016llx:%016llx %11lld",
264e7f926a5SMatthew Dillon 				(long long)mirror.key_cur.obj_id,
265e7f926a5SMatthew Dillon 				(long long)mirror.tid_beg,
266e7f926a5SMatthew Dillon 				(long long)mirror.tid_end,
26748eadef9SMatthew Dillon 				total_bytes);
26848eadef9SMatthew Dillon 			fflush(stderr);
26948eadef9SMatthew Dillon 		}
270a7fbbf91SMatthew Dillon 		mirror.key_beg = mirror.key_cur;
271e7f926a5SMatthew Dillon 
272e7f926a5SMatthew Dillon 		/*
273e7f926a5SMatthew Dillon 		 * Deal with time limit option
274e7f926a5SMatthew Dillon 		 */
275243ca327SMatthew Dillon 		if (TimeoutOpt &&
276243ca327SMatthew Dillon 		    (unsigned)(time(NULL) - base_t) > (unsigned)TimeoutOpt) {
277243ca327SMatthew Dillon 			fprintf(stderr,
278243ca327SMatthew Dillon 				"Mirror-read %s interrupted by timer at"
279243ca327SMatthew Dillon 				" %016llx\n",
280243ca327SMatthew Dillon 				filesystem,
281243ca327SMatthew Dillon 				mirror.key_cur.obj_id);
282243ca327SMatthew Dillon 			interrupted = 1;
283243ca327SMatthew Dillon 			break;
284243ca327SMatthew Dillon 		}
285a7fbbf91SMatthew Dillon 	} while (mirror.count != 0);
286a7fbbf91SMatthew Dillon 
28748eadef9SMatthew Dillon done:
288243ca327SMatthew Dillon 	/*
28948eadef9SMatthew Dillon 	 * Write out the termination sync record - only if not interrupted
290243ca327SMatthew Dillon 	 */
29148eadef9SMatthew Dillon 	if (interrupted == 0) {
29248eadef9SMatthew Dillon 		if (didwork) {
29317dd83bcSMatthew Dillon 			write_mrecord(1, HAMMER_MREC_TYPE_SYNC,
29417dd83bcSMatthew Dillon 				      &mrec_tmp, sizeof(mrec_tmp.sync));
29548eadef9SMatthew Dillon 		} else {
29648eadef9SMatthew Dillon 			write_mrecord(1, HAMMER_MREC_TYPE_IDLE,
29748eadef9SMatthew Dillon 				      &mrec_tmp, sizeof(mrec_tmp.sync));
29848eadef9SMatthew Dillon 		}
29948eadef9SMatthew Dillon 	}
30034ebae70SMatthew Dillon 
301243ca327SMatthew Dillon 	/*
302243ca327SMatthew Dillon 	 * If the -2 option was given (automatic when doing mirror-copy),
303243ca327SMatthew Dillon 	 * a two-way pipe is assumed and we expect a response mrec from
304243ca327SMatthew Dillon 	 * the target.
305243ca327SMatthew Dillon 	 */
306243ca327SMatthew Dillon 	if (TwoWayPipeOpt) {
30748eadef9SMatthew Dillon 		mrec = read_mrecord(0, &error, &pickup);
30817dd83bcSMatthew Dillon 		if (mrec == NULL ||
30917dd83bcSMatthew Dillon 		    mrec->head.type != HAMMER_MREC_TYPE_UPDATE ||
31017dd83bcSMatthew Dillon 		    mrec->head.rec_size != sizeof(mrec->update)) {
311243ca327SMatthew Dillon 			fprintf(stderr, "mirror_read: Did not get final "
312243ca327SMatthew Dillon 					"acknowledgement packet from target\n");
313243ca327SMatthew Dillon 			exit(1);
314243ca327SMatthew Dillon 		}
315243ca327SMatthew Dillon 		if (interrupted) {
316243ca327SMatthew Dillon 			if (CyclePath) {
317243ca327SMatthew Dillon 				hammer_set_cycle(&mirror.key_cur, mirror.tid_beg);
31834bb69d8SThomas Nikolajsen 				fprintf(stderr, "Cyclefile %s updated for "
31934bb69d8SThomas Nikolajsen 					"continuation\n", CyclePath);
320243ca327SMatthew Dillon 			}
321243ca327SMatthew Dillon 		} else {
32217dd83bcSMatthew Dillon 			sync_tid = mrec->update.tid;
323243ca327SMatthew Dillon 			if (CyclePath) {
324243ca327SMatthew Dillon 				hammer_key_beg_init(&mirror.key_beg);
325243ca327SMatthew Dillon 				hammer_set_cycle(&mirror.key_beg, sync_tid);
326243ca327SMatthew Dillon 				fprintf(stderr, "Cyclefile %s updated to 0x%016llx\n",
327243ca327SMatthew Dillon 					CyclePath, sync_tid);
328243ca327SMatthew Dillon 			}
329243ca327SMatthew Dillon 		}
330243ca327SMatthew Dillon 	} else if (CyclePath) {
331243ca327SMatthew Dillon 		/* NOTE! mirror.tid_beg cannot be updated */
332243ca327SMatthew Dillon 		fprintf(stderr, "Warning: cycle file (-c option) cannot be "
333243ca327SMatthew Dillon 				"fully updated unless you use mirror-copy\n");
334243ca327SMatthew Dillon 		hammer_set_cycle(&mirror.key_beg, mirror.tid_beg);
335243ca327SMatthew Dillon 	}
33648eadef9SMatthew Dillon 	if (streaming && interrupted == 0) {
33748eadef9SMatthew Dillon 		time_t t1 = time(NULL);
33848eadef9SMatthew Dillon 		time_t t2;
33948eadef9SMatthew Dillon 
340e7f926a5SMatthew Dillon 		/*
341e7f926a5SMatthew Dillon 		 * Two way streaming tries to break down large bulk
342e7f926a5SMatthew Dillon 		 * transfers into smaller ones so it can sync the
343e7f926a5SMatthew Dillon 		 * transaction id on the slave.  This way if we get
344e7f926a5SMatthew Dillon 		 * interrupted a restart doesn't have to start from
345e7f926a5SMatthew Dillon 		 * scratch.
346e7f926a5SMatthew Dillon 		 */
347e7f926a5SMatthew Dillon 		if (TwoWayPipeOpt && streaming && histogram > 0) {
348e7f926a5SMatthew Dillon 			if (VerboseOpt)
349e7f926a5SMatthew Dillon 				fprintf(stderr, " (bulk incremental)");
350e7f926a5SMatthew Dillon 			goto again;
351e7f926a5SMatthew Dillon 		}
352e7f926a5SMatthew Dillon 
35348eadef9SMatthew Dillon 		if (VerboseOpt) {
35448eadef9SMatthew Dillon 			fprintf(stderr, " W");
35548eadef9SMatthew Dillon 			fflush(stderr);
35648eadef9SMatthew Dillon 		}
35748eadef9SMatthew Dillon 		pfs.ondisk->sync_end_tid = mirror.tid_end;
35848eadef9SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_WAI_PSEUDOFS, &pfs) < 0) {
35948eadef9SMatthew Dillon 			fprintf(stderr, "Mirror-read %s: cannot stream: %s\n",
36048eadef9SMatthew Dillon 				filesystem, strerror(errno));
36148eadef9SMatthew Dillon 		} else {
36248eadef9SMatthew Dillon 			t2 = time(NULL) - t1;
36348eadef9SMatthew Dillon 			if (t2 >= 0 && t2 < DelayOpt) {
36448eadef9SMatthew Dillon 				if (VerboseOpt) {
36548eadef9SMatthew Dillon 					fprintf(stderr, "\bD");
36648eadef9SMatthew Dillon 					fflush(stderr);
36748eadef9SMatthew Dillon 				}
36848eadef9SMatthew Dillon 				sleep(DelayOpt - t2);
36948eadef9SMatthew Dillon 			}
37048eadef9SMatthew Dillon 			if (VerboseOpt) {
37148eadef9SMatthew Dillon 				fprintf(stderr, "\b ");
37248eadef9SMatthew Dillon 				fflush(stderr);
37348eadef9SMatthew Dillon 			}
37448eadef9SMatthew Dillon 			relpfs(fd, &pfs);
37548eadef9SMatthew Dillon 			goto again;
37648eadef9SMatthew Dillon 		}
37748eadef9SMatthew Dillon 	}
37848eadef9SMatthew Dillon 	write_mrecord(1, HAMMER_MREC_TYPE_TERM,
37948eadef9SMatthew Dillon 		      &mrec_tmp, sizeof(mrec_tmp.sync));
38048eadef9SMatthew Dillon 	relpfs(fd, &pfs);
381a7fbbf91SMatthew Dillon 	fprintf(stderr, "Mirror-read %s succeeded\n", filesystem);
382a7fbbf91SMatthew Dillon }
383a7fbbf91SMatthew Dillon 
384e7f926a5SMatthew Dillon /*
385e7f926a5SMatthew Dillon  * Ok, this isn't really a histogram.  What we are trying to do
386e7f926a5SMatthew Dillon  * here is find the first tid_end for the scan that returns
387e7f926a5SMatthew Dillon  * at least some data.  The frontend of the TID space will generally
388e7f926a5SMatthew Dillon  * return nothing so we can't just divide out the full mirroring
389e7f926a5SMatthew Dillon  * range.  Once we find the point where a real data stream starts
390e7f926a5SMatthew Dillon  * to get generated we can divide out the range from that point.
391e7f926a5SMatthew Dillon  *
392e7f926a5SMatthew Dillon  * When starting a new mirroring operation completely from scratch
393e7f926a5SMatthew Dillon  * this code will take some time to run, but once some mirroring
394e7f926a5SMatthew Dillon  * data is synchronized on the target you will be able to interrupt
395e7f926a5SMatthew Dillon  * the stream and restart it and the later invocations of this
396e7f926a5SMatthew Dillon  * code will be such that it should run much faster.
397e7f926a5SMatthew Dillon  */
398e7f926a5SMatthew Dillon static int
399e7f926a5SMatthew Dillon generate_histogram(int fd, const char *filesystem,
400e7f926a5SMatthew Dillon 		   hammer_tid_t **histogram_ary,
401e7f926a5SMatthew Dillon 		   struct hammer_ioc_mirror_rw *mirror_base)
402e7f926a5SMatthew Dillon {
403e7f926a5SMatthew Dillon 	struct hammer_ioc_mirror_rw mirror;
404e7f926a5SMatthew Dillon 	hammer_tid_t tid_beg;
405e7f926a5SMatthew Dillon 	hammer_tid_t tid_end;
406e7f926a5SMatthew Dillon 	hammer_tid_t tid_half;
407e7f926a5SMatthew Dillon 	int i;
408e7f926a5SMatthew Dillon 
409e7f926a5SMatthew Dillon 	mirror = *mirror_base;
410e7f926a5SMatthew Dillon 	tid_beg = mirror.tid_beg;
411e7f926a5SMatthew Dillon 	tid_end = mirror.tid_end;
412e7f926a5SMatthew Dillon 
413e7f926a5SMatthew Dillon 	if (*histogram_ary)
414e7f926a5SMatthew Dillon 		free(*histogram_ary);
415e7f926a5SMatthew Dillon 	if (tid_beg + BULK_MINIMUM >= tid_end)
416e7f926a5SMatthew Dillon 		return(0);
417e7f926a5SMatthew Dillon 
418e7f926a5SMatthew Dillon 	if (VerboseOpt)
419e7f926a5SMatthew Dillon 		fprintf(stderr, "Doing Range Test\n");
420e7f926a5SMatthew Dillon 	while (tid_end - tid_beg > BULK_MINIMUM) {
421e7f926a5SMatthew Dillon 		tid_half = tid_beg + (tid_end - tid_beg) * 2 / 3;
422e7f926a5SMatthew Dillon 		mirror.count = 0;
423e7f926a5SMatthew Dillon 		mirror.tid_beg = tid_beg;
424e7f926a5SMatthew Dillon 		mirror.tid_end = tid_half;
425e7f926a5SMatthew Dillon 
426e7f926a5SMatthew Dillon 		if (VerboseOpt > 1) {
427e7f926a5SMatthew Dillon 			fprintf(stderr, "RangeTest %016llx/%016llx - %016llx (%lld) ",
428e7f926a5SMatthew Dillon 				(long long)tid_beg,
429e7f926a5SMatthew Dillon 				(long long)tid_end,
430e7f926a5SMatthew Dillon 				(long long)tid_half,
431e7f926a5SMatthew Dillon 				(long long)(tid_half - tid_beg));
432e7f926a5SMatthew Dillon 		}
433e7f926a5SMatthew Dillon 		fflush(stderr);
434e7f926a5SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) {
435e7f926a5SMatthew Dillon 			fprintf(stderr, "Mirror-read %s failed: %s\n",
436e7f926a5SMatthew Dillon 				filesystem, strerror(errno));
437e7f926a5SMatthew Dillon 			exit(1);
438e7f926a5SMatthew Dillon 		}
439e7f926a5SMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) {
440e7f926a5SMatthew Dillon 			fprintf(stderr,
441e7f926a5SMatthew Dillon 				"Mirror-read %s fatal error %d\n",
442e7f926a5SMatthew Dillon 				filesystem, mirror.head.error);
443e7f926a5SMatthew Dillon 			exit(1);
444e7f926a5SMatthew Dillon 		}
445e7f926a5SMatthew Dillon 		if (VerboseOpt > 1)
446e7f926a5SMatthew Dillon 			fprintf(stderr, "%d\n", mirror.count);
447e7f926a5SMatthew Dillon 		if (mirror.count > SERIALBUF_SIZE / 2) {
448e7f926a5SMatthew Dillon 			tid_end = tid_half;
449e7f926a5SMatthew Dillon 		} else {
450e7f926a5SMatthew Dillon 			tid_beg = tid_half;
451e7f926a5SMatthew Dillon 		}
452e7f926a5SMatthew Dillon 	}
453e7f926a5SMatthew Dillon 
454e7f926a5SMatthew Dillon 	tid_end = mirror_base->tid_end;
455e7f926a5SMatthew Dillon 	fprintf(stderr, "histogram range %016llx - %016llx\n",
456e7f926a5SMatthew Dillon 		(long long)tid_beg, (long long)tid_end);
457e7f926a5SMatthew Dillon 
458e7f926a5SMatthew Dillon 	/*
459e7f926a5SMatthew Dillon 	 * The final array generates our incremental ending tids in
460e7f926a5SMatthew Dillon 	 * reverse order.  The caller also picks them off in reverse order.
461e7f926a5SMatthew Dillon 	 */
462e7f926a5SMatthew Dillon 	*histogram_ary = malloc(sizeof(hammer_tid_t) * 20);
463e7f926a5SMatthew Dillon 	for (i = 0; i < 20; ++i) {
464e7f926a5SMatthew Dillon 		(*histogram_ary)[i] = tid_end - (tid_end - tid_beg) / 20 * i;
465e7f926a5SMatthew Dillon 	}
466e7f926a5SMatthew Dillon 	return(20);
467e7f926a5SMatthew Dillon }
468e7f926a5SMatthew Dillon 
46901a72c9fSMatthew Dillon static void
47001a72c9fSMatthew Dillon create_pfs(const char *filesystem, uuid_t *s_uuid)
47101a72c9fSMatthew Dillon {
472f414d101SSascha Wildner 	if (ForceYesOpt == 1) {
47307485271SMichael Neumann 		fprintf(stderr, "PFS slave %s does not exist. "
47407485271SMichael Neumann 			"Auto create new slave PFS!\n", filesystem);
47507485271SMichael Neumann 
476f414d101SSascha Wildner 	} else {
47701a72c9fSMatthew Dillon 		fprintf(stderr, "PFS slave %s does not exist.\n"
47801a72c9fSMatthew Dillon 			"Do you want to create a new slave PFS? (yes|no) ",
47901a72c9fSMatthew Dillon 			filesystem);
48001a72c9fSMatthew Dillon 		fflush(stderr);
48101a72c9fSMatthew Dillon 		if (getyn() != 1) {
48201a72c9fSMatthew Dillon 			fprintf(stderr, "Aborting operation\n");
48301a72c9fSMatthew Dillon 			exit(1);
48401a72c9fSMatthew Dillon 		}
48507485271SMichael Neumann 	}
48601a72c9fSMatthew Dillon 
48701a72c9fSMatthew Dillon 	u_int32_t status;
48801a72c9fSMatthew Dillon 	char *shared_uuid = NULL;
48901a72c9fSMatthew Dillon 	uuid_to_string(s_uuid, &shared_uuid, &status);
49001a72c9fSMatthew Dillon 
49101a72c9fSMatthew Dillon 	char *cmd = NULL;
49201a72c9fSMatthew Dillon 	asprintf(&cmd, "/sbin/hammer pfs-slave '%s' shared-uuid=%s 1>&2",
49301a72c9fSMatthew Dillon 		 filesystem, shared_uuid);
49401a72c9fSMatthew Dillon 	free(shared_uuid);
49501a72c9fSMatthew Dillon 
49601a72c9fSMatthew Dillon 	if (cmd == NULL) {
49701a72c9fSMatthew Dillon 		fprintf(stderr, "Failed to alloc memory\n");
49801a72c9fSMatthew Dillon 		exit(1);
49901a72c9fSMatthew Dillon 	}
50001a72c9fSMatthew Dillon 	if (system(cmd) != 0) {
50101a72c9fSMatthew Dillon 		fprintf(stderr, "Failed to create PFS\n");
50201a72c9fSMatthew Dillon 	}
50301a72c9fSMatthew Dillon 	free(cmd);
50401a72c9fSMatthew Dillon }
50501a72c9fSMatthew Dillon 
50617dd83bcSMatthew Dillon /*
50717dd83bcSMatthew Dillon  * Pipe the mirroring data stream on stdin to the HAMMER VFS, adding
50817dd83bcSMatthew Dillon  * some additional packet types to negotiate TID ranges and to verify
50917dd83bcSMatthew Dillon  * completion.  The HAMMER VFS does most of the work.
51017dd83bcSMatthew Dillon  *
51117dd83bcSMatthew Dillon  * It is important to note that the mirror.key_{beg,end} range must
51217dd83bcSMatthew Dillon  * match the ranged used by the original.  For now both sides use
51317dd83bcSMatthew Dillon  * range the entire key space.
51417dd83bcSMatthew Dillon  *
51517dd83bcSMatthew Dillon  * It is even more important that the records in the stream conform
51617dd83bcSMatthew Dillon  * to the TID range also supplied in the stream.  The HAMMER VFS will
51717dd83bcSMatthew Dillon  * use the REC, PASS, and SKIP record types to track the portions of
51817dd83bcSMatthew Dillon  * the B-Tree being scanned in order to be able to proactively delete
51917dd83bcSMatthew Dillon  * records on the target within those active areas that are not mentioned
52017dd83bcSMatthew Dillon  * by the source.
52117dd83bcSMatthew Dillon  *
52217dd83bcSMatthew Dillon  * The mirror.key_cur field is used by the VFS to do this tracking.  It
52317dd83bcSMatthew Dillon  * must be initialized to key_beg but then is persistently updated by
52417dd83bcSMatthew Dillon  * the HAMMER VFS on each successive ioctl() call.  If you blow up this
52517dd83bcSMatthew Dillon  * field you will blow up the mirror target, possibly to the point of
52617dd83bcSMatthew Dillon  * deleting everything.  As a safety measure the HAMMER VFS simply marks
52717dd83bcSMatthew Dillon  * the records that the source has destroyed as deleted on the target,
52817dd83bcSMatthew Dillon  * and normal pruning operations will deal with their final disposition
52917dd83bcSMatthew Dillon  * at some later time.
53017dd83bcSMatthew Dillon  */
531a7fbbf91SMatthew Dillon void
532a7fbbf91SMatthew Dillon hammer_cmd_mirror_write(char **av, int ac)
533a7fbbf91SMatthew Dillon {
534a7fbbf91SMatthew Dillon 	struct hammer_ioc_mirror_rw mirror;
535a7fbbf91SMatthew Dillon 	const char *filesystem;
536a7fbbf91SMatthew Dillon 	char *buf = malloc(SERIALBUF_SIZE);
537d4e5b69bSMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
53817dd83bcSMatthew Dillon 	struct hammer_ioc_mrecord_head pickup;
539243ca327SMatthew Dillon 	struct hammer_ioc_synctid synctid;
54017dd83bcSMatthew Dillon 	union hammer_ioc_mrecord_any mrec_tmp;
54117dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
54201a72c9fSMatthew Dillon 	struct stat st;
543243ca327SMatthew Dillon 	int error;
544243ca327SMatthew Dillon 	int fd;
54548eadef9SMatthew Dillon 	int n;
546a7fbbf91SMatthew Dillon 
54734bb69d8SThomas Nikolajsen 	if (ac != 1)
548a7fbbf91SMatthew Dillon 		mirror_usage(1);
549a7fbbf91SMatthew Dillon 	filesystem = av[0];
550a7fbbf91SMatthew Dillon 
55148eadef9SMatthew Dillon 	pickup.signature = 0;
55248eadef9SMatthew Dillon 	pickup.type = 0;
55348eadef9SMatthew Dillon 
55448eadef9SMatthew Dillon again:
555a7fbbf91SMatthew Dillon 	bzero(&mirror, sizeof(mirror));
556a7fbbf91SMatthew Dillon 	hammer_key_beg_init(&mirror.key_beg);
557a7fbbf91SMatthew Dillon 	hammer_key_end_init(&mirror.key_end);
55817dd83bcSMatthew Dillon 	mirror.key_end = mirror.key_beg;
559a7fbbf91SMatthew Dillon 
56001a72c9fSMatthew Dillon 	/*
56101a72c9fSMatthew Dillon 	 * Read initial packet
56201a72c9fSMatthew Dillon 	 */
56301a72c9fSMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
56401a72c9fSMatthew Dillon 	if (mrec == NULL) {
56501a72c9fSMatthew Dillon 		if (error == 0)
56601a72c9fSMatthew Dillon 			fprintf(stderr, "validate_mrec_header: short read\n");
56701a72c9fSMatthew Dillon 		exit(1);
56801a72c9fSMatthew Dillon 	}
56901a72c9fSMatthew Dillon 	/*
57001a72c9fSMatthew Dillon 	 * Validate packet
57101a72c9fSMatthew Dillon 	 */
57201a72c9fSMatthew Dillon 	if (mrec->head.type == HAMMER_MREC_TYPE_TERM) {
57301a72c9fSMatthew Dillon 		return;
57401a72c9fSMatthew Dillon 	}
57501a72c9fSMatthew Dillon 	if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) {
57601a72c9fSMatthew Dillon 		fprintf(stderr, "validate_mrec_header: did not get expected "
57701a72c9fSMatthew Dillon 				"PFSD record type\n");
57801a72c9fSMatthew Dillon 		exit(1);
57901a72c9fSMatthew Dillon 	}
58001a72c9fSMatthew Dillon 	if (mrec->head.rec_size != sizeof(mrec->pfs)) {
58101a72c9fSMatthew Dillon 		fprintf(stderr, "validate_mrec_header: unexpected payload "
58201a72c9fSMatthew Dillon 				"size\n");
58301a72c9fSMatthew Dillon 		exit(1);
58401a72c9fSMatthew Dillon 	}
58501a72c9fSMatthew Dillon 
58601a72c9fSMatthew Dillon 	/*
58701a72c9fSMatthew Dillon 	 * Create slave PFS if it doesn't yet exist
58801a72c9fSMatthew Dillon 	 */
58901a72c9fSMatthew Dillon 	if (lstat(filesystem, &st) != 0) {
59001a72c9fSMatthew Dillon 		create_pfs(filesystem, &mrec->pfs.pfsd.shared_uuid);
59101a72c9fSMatthew Dillon 	}
59201a72c9fSMatthew Dillon 	free(mrec);
59301a72c9fSMatthew Dillon 	mrec = NULL;
59401a72c9fSMatthew Dillon 
595d4e5b69bSMatthew Dillon 	fd = getpfs(&pfs, filesystem);
596a7fbbf91SMatthew Dillon 
597243ca327SMatthew Dillon 	/*
598d4e5b69bSMatthew Dillon 	 * In two-way mode the target writes out a PFS packet first.
599d4e5b69bSMatthew Dillon 	 * The source uses our tid_end as its tid_beg by default,
600d4e5b69bSMatthew Dillon 	 * picking up where it left off.
601243ca327SMatthew Dillon 	 */
602d4e5b69bSMatthew Dillon 	mirror.tid_beg = 0;
603d4e5b69bSMatthew Dillon 	if (TwoWayPipeOpt) {
604e7f926a5SMatthew Dillon 		generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp);
605e7f926a5SMatthew Dillon 		if (mirror.tid_beg < mrec_tmp.pfs.pfsd.sync_beg_tid)
606e7f926a5SMatthew Dillon 			mirror.tid_beg = mrec_tmp.pfs.pfsd.sync_beg_tid;
607e7f926a5SMatthew Dillon 		mirror.tid_end = mrec_tmp.pfs.pfsd.sync_end_tid;
608e7f926a5SMatthew Dillon 		write_mrecord(1, HAMMER_MREC_TYPE_PFSD,
609e7f926a5SMatthew Dillon 			      &mrec_tmp, sizeof(mrec_tmp.pfs));
610d4e5b69bSMatthew Dillon 	}
611d4e5b69bSMatthew Dillon 
612d4e5b69bSMatthew Dillon 	/*
61317dd83bcSMatthew Dillon 	 * Read and process the PFS header.  The source informs us of
61417dd83bcSMatthew Dillon 	 * the TID range the stream represents.
615d4e5b69bSMatthew Dillon 	 */
61648eadef9SMatthew Dillon 	n = validate_mrec_header(fd, 0, 1, pfs.pfs_id, &pickup,
617d4e5b69bSMatthew Dillon 				 &mirror.tid_beg, &mirror.tid_end);
61848eadef9SMatthew Dillon 	if (n < 0) {	/* got TERM record */
61948eadef9SMatthew Dillon 		relpfs(fd, &pfs);
62048eadef9SMatthew Dillon 		return;
62148eadef9SMatthew Dillon 	}
62234ebae70SMatthew Dillon 
623a7fbbf91SMatthew Dillon 	mirror.ubuf = buf;
624a7fbbf91SMatthew Dillon 	mirror.size = SERIALBUF_SIZE;
625a7fbbf91SMatthew Dillon 
626243ca327SMatthew Dillon 	/*
62717dd83bcSMatthew Dillon 	 * Read and process bulk records (REC, PASS, and SKIP types).
62817dd83bcSMatthew Dillon 	 *
62917dd83bcSMatthew Dillon 	 * On your life, do NOT mess with mirror.key_cur or your mirror
63017dd83bcSMatthew Dillon 	 * target may become history.
631243ca327SMatthew Dillon 	 */
632a7fbbf91SMatthew Dillon 	for (;;) {
633a7fbbf91SMatthew Dillon 		mirror.count = 0;
634d4e5b69bSMatthew Dillon 		mirror.pfs_id = pfs.pfs_id;
635d4e5b69bSMatthew Dillon 		mirror.shared_uuid = pfs.ondisk->shared_uuid;
636a7fbbf91SMatthew Dillon 		mirror.size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup);
637a7fbbf91SMatthew Dillon 		if (mirror.size <= 0)
638a7fbbf91SMatthew Dillon 			break;
639a7fbbf91SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_MIRROR_WRITE, &mirror) < 0) {
640a7fbbf91SMatthew Dillon 			fprintf(stderr, "Mirror-write %s failed: %s\n",
641a7fbbf91SMatthew Dillon 				filesystem, strerror(errno));
642a7fbbf91SMatthew Dillon 			exit(1);
643a7fbbf91SMatthew Dillon 		}
64417dd83bcSMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) {
64517dd83bcSMatthew Dillon 			fprintf(stderr,
64617dd83bcSMatthew Dillon 				"Mirror-write %s fatal error %d\n",
64717dd83bcSMatthew Dillon 				filesystem, mirror.head.error);
64817dd83bcSMatthew Dillon 			exit(1);
64917dd83bcSMatthew Dillon 		}
650243ca327SMatthew Dillon #if 0
651a7fbbf91SMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_INTR) {
652a7fbbf91SMatthew Dillon 			fprintf(stderr,
653a7fbbf91SMatthew Dillon 				"Mirror-write %s interrupted by timer at"
654243ca327SMatthew Dillon 				" %016llx\n",
655a7fbbf91SMatthew Dillon 				filesystem,
656243ca327SMatthew Dillon 				mirror.key_cur.obj_id);
657a7fbbf91SMatthew Dillon 			exit(0);
658a7fbbf91SMatthew Dillon 		}
659243ca327SMatthew Dillon #endif
660a7fbbf91SMatthew Dillon 	}
661243ca327SMatthew Dillon 
662243ca327SMatthew Dillon 	/*
663243ca327SMatthew Dillon 	 * Read and process the termination sync record.
664243ca327SMatthew Dillon 	 */
665243ca327SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
6669dc76cb1SMatthew Dillon 
6679dc76cb1SMatthew Dillon 	if (mrec && mrec->head.type == HAMMER_MREC_TYPE_TERM) {
66848eadef9SMatthew Dillon 		fprintf(stderr, "Mirror-write: received termination request\n");
66948eadef9SMatthew Dillon 		free(mrec);
6709dc76cb1SMatthew Dillon 		return;
6719dc76cb1SMatthew Dillon 	}
6729dc76cb1SMatthew Dillon 
67317dd83bcSMatthew Dillon 	if (mrec == NULL ||
67448eadef9SMatthew Dillon 	    (mrec->head.type != HAMMER_MREC_TYPE_SYNC &&
67548eadef9SMatthew Dillon 	     mrec->head.type != HAMMER_MREC_TYPE_IDLE) ||
67617dd83bcSMatthew Dillon 	    mrec->head.rec_size != sizeof(mrec->sync)) {
677243ca327SMatthew Dillon 		fprintf(stderr, "Mirror-write %s: Did not get termination "
67817dd83bcSMatthew Dillon 				"sync record, or rec_size is wrong rt=%d\n",
67917dd83bcSMatthew Dillon 				filesystem, mrec->head.type);
680da44aa75SMatthew Dillon 		exit(1);
681243ca327SMatthew Dillon 	}
682243ca327SMatthew Dillon 
683243ca327SMatthew Dillon 	/*
684243ca327SMatthew Dillon 	 * Update the PFS info on the target so the user has visibility
68548eadef9SMatthew Dillon 	 * into the new snapshot, and sync the target filesystem.
686243ca327SMatthew Dillon 	 */
68748eadef9SMatthew Dillon 	if (mrec->head.type == HAMMER_MREC_TYPE_SYNC) {
688d4e5b69bSMatthew Dillon 		update_pfs_snapshot(fd, mirror.tid_end, pfs.pfs_id);
689243ca327SMatthew Dillon 
690243ca327SMatthew Dillon 		bzero(&synctid, sizeof(synctid));
691243ca327SMatthew Dillon 		synctid.op = HAMMER_SYNCTID_SYNC2;
692243ca327SMatthew Dillon 		ioctl(fd, HAMMERIOC_SYNCTID, &synctid);
693243ca327SMatthew Dillon 
69448eadef9SMatthew Dillon 		if (VerboseOpt >= 2) {
69548eadef9SMatthew Dillon 			fprintf(stderr, "Mirror-write %s: succeeded\n",
69648eadef9SMatthew Dillon 				filesystem);
69748eadef9SMatthew Dillon 		}
69848eadef9SMatthew Dillon 	}
69948eadef9SMatthew Dillon 
70048eadef9SMatthew Dillon 	free(mrec);
70148eadef9SMatthew Dillon 	mrec = NULL;
702243ca327SMatthew Dillon 
703243ca327SMatthew Dillon 	/*
704243ca327SMatthew Dillon 	 * Report back to the originator.
705243ca327SMatthew Dillon 	 */
706243ca327SMatthew Dillon 	if (TwoWayPipeOpt) {
70717dd83bcSMatthew Dillon 		mrec_tmp.update.tid = mirror.tid_end;
708243ca327SMatthew Dillon 		write_mrecord(1, HAMMER_MREC_TYPE_UPDATE,
70917dd83bcSMatthew Dillon 			      &mrec_tmp, sizeof(mrec_tmp.update));
710243ca327SMatthew Dillon 	} else {
711243ca327SMatthew Dillon 		printf("Source can update synctid to 0x%016llx\n",
712243ca327SMatthew Dillon 		       mirror.tid_end);
713243ca327SMatthew Dillon 	}
71448eadef9SMatthew Dillon 	relpfs(fd, &pfs);
71548eadef9SMatthew Dillon 	goto again;
716243ca327SMatthew Dillon }
717243ca327SMatthew Dillon 
718243ca327SMatthew Dillon void
719243ca327SMatthew Dillon hammer_cmd_mirror_dump(void)
720243ca327SMatthew Dillon {
721243ca327SMatthew Dillon 	char *buf = malloc(SERIALBUF_SIZE);
72217dd83bcSMatthew Dillon 	struct hammer_ioc_mrecord_head pickup;
72317dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
724243ca327SMatthew Dillon 	int error;
725243ca327SMatthew Dillon 	int size;
72617dd83bcSMatthew Dillon 	int offset;
72717dd83bcSMatthew Dillon 	int bytes;
728243ca327SMatthew Dillon 
729243ca327SMatthew Dillon 	/*
730243ca327SMatthew Dillon 	 * Read and process the PFS header
731243ca327SMatthew Dillon 	 */
732243ca327SMatthew Dillon 	pickup.signature = 0;
733243ca327SMatthew Dillon 	pickup.type = 0;
734243ca327SMatthew Dillon 
735243ca327SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
736243ca327SMatthew Dillon 
737243ca327SMatthew Dillon 	/*
738243ca327SMatthew Dillon 	 * Read and process bulk records
739243ca327SMatthew Dillon 	 */
740243ca327SMatthew Dillon 	for (;;) {
741243ca327SMatthew Dillon 		size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup);
742243ca327SMatthew Dillon 		if (size <= 0)
743243ca327SMatthew Dillon 			break;
74417dd83bcSMatthew Dillon 		offset = 0;
74517dd83bcSMatthew Dillon 		while (offset < size) {
74617dd83bcSMatthew Dillon 			mrec = (void *)((char *)buf + offset);
74717dd83bcSMatthew Dillon 			bytes = HAMMER_HEAD_DOALIGN(mrec->head.rec_size);
74817dd83bcSMatthew Dillon 			if (offset + bytes > size) {
74917dd83bcSMatthew Dillon 				fprintf(stderr, "Misaligned record\n");
75017dd83bcSMatthew Dillon 				exit(1);
75117dd83bcSMatthew Dillon 			}
75217dd83bcSMatthew Dillon 
75385a8e8a7SMatthew Dillon 			switch(mrec->head.type & HAMMER_MRECF_TYPE_MASK) {
75485a8e8a7SMatthew Dillon 			case HAMMER_MREC_TYPE_REC_BADCRC:
75517dd83bcSMatthew Dillon 			case HAMMER_MREC_TYPE_REC:
756243ca327SMatthew Dillon 				printf("Record obj=%016llx key=%016llx "
75785a8e8a7SMatthew Dillon 				       "rt=%02x ot=%02x",
75817dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_id,
75917dd83bcSMatthew Dillon 					mrec->rec.leaf.base.key,
76017dd83bcSMatthew Dillon 					mrec->rec.leaf.base.rec_type,
76117dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_type);
76285a8e8a7SMatthew Dillon 				if (mrec->head.type ==
76385a8e8a7SMatthew Dillon 				    HAMMER_MREC_TYPE_REC_BADCRC) {
76485a8e8a7SMatthew Dillon 					printf(" (BAD CRC)");
76585a8e8a7SMatthew Dillon 				}
76685a8e8a7SMatthew Dillon 				printf("\n");
767243ca327SMatthew Dillon 				printf("       tids %016llx:%016llx data=%d\n",
76817dd83bcSMatthew Dillon 					mrec->rec.leaf.base.create_tid,
76917dd83bcSMatthew Dillon 					mrec->rec.leaf.base.delete_tid,
77017dd83bcSMatthew Dillon 					mrec->rec.leaf.data_len);
77117dd83bcSMatthew Dillon 				break;
77217dd83bcSMatthew Dillon 			case HAMMER_MREC_TYPE_PASS:
77317dd83bcSMatthew Dillon 				printf("Pass   obj=%016llx key=%016llx "
77417dd83bcSMatthew Dillon 				       "rt=%02x ot=%02x\n",
77517dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_id,
77617dd83bcSMatthew Dillon 					mrec->rec.leaf.base.key,
77717dd83bcSMatthew Dillon 					mrec->rec.leaf.base.rec_type,
77817dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_type);
77917dd83bcSMatthew Dillon 				printf("       tids %016llx:%016llx data=%d\n",
78017dd83bcSMatthew Dillon 					mrec->rec.leaf.base.create_tid,
78117dd83bcSMatthew Dillon 					mrec->rec.leaf.base.delete_tid,
78217dd83bcSMatthew Dillon 					mrec->rec.leaf.data_len);
78317dd83bcSMatthew Dillon 				break;
78417dd83bcSMatthew Dillon 			case HAMMER_MREC_TYPE_SKIP:
78517dd83bcSMatthew Dillon 				printf("Skip   obj=%016llx key=%016llx rt=%02x to\n"
78617dd83bcSMatthew Dillon 				       "       obj=%016llx key=%016llx rt=%02x\n",
78717dd83bcSMatthew Dillon 				       mrec->skip.skip_beg.obj_id,
78817dd83bcSMatthew Dillon 				       mrec->skip.skip_beg.key,
78917dd83bcSMatthew Dillon 				       mrec->skip.skip_beg.rec_type,
79017dd83bcSMatthew Dillon 				       mrec->skip.skip_end.obj_id,
79117dd83bcSMatthew Dillon 				       mrec->skip.skip_end.key,
79217dd83bcSMatthew Dillon 				       mrec->skip.skip_end.rec_type);
79317dd83bcSMatthew Dillon 			default:
79417dd83bcSMatthew Dillon 				break;
79517dd83bcSMatthew Dillon 			}
79617dd83bcSMatthew Dillon 			offset += bytes;
797243ca327SMatthew Dillon 		}
798243ca327SMatthew Dillon 	}
799243ca327SMatthew Dillon 
800243ca327SMatthew Dillon 	/*
801243ca327SMatthew Dillon 	 * Read and process the termination sync record.
802243ca327SMatthew Dillon 	 */
803243ca327SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
80448eadef9SMatthew Dillon 	if (mrec == NULL ||
80548eadef9SMatthew Dillon 	    (mrec->head.type != HAMMER_MREC_TYPE_SYNC &&
80648eadef9SMatthew Dillon 	     mrec->head.type != HAMMER_MREC_TYPE_IDLE)
80748eadef9SMatthew Dillon 	 ) {
808243ca327SMatthew Dillon 		fprintf(stderr, "Mirror-dump: Did not get termination "
809243ca327SMatthew Dillon 				"sync record\n");
810243ca327SMatthew Dillon 	}
811a7fbbf91SMatthew Dillon }
812a7fbbf91SMatthew Dillon 
813a7fbbf91SMatthew Dillon void
81448eadef9SMatthew Dillon hammer_cmd_mirror_copy(char **av, int ac, int streaming)
815a7fbbf91SMatthew Dillon {
81634ebae70SMatthew Dillon 	pid_t pid1;
81734ebae70SMatthew Dillon 	pid_t pid2;
81834ebae70SMatthew Dillon 	int fds[2];
819243ca327SMatthew Dillon 	const char *xav[16];
820243ca327SMatthew Dillon 	char tbuf[16];
82134ebae70SMatthew Dillon 	char *ptr;
822243ca327SMatthew Dillon 	int xac;
82334ebae70SMatthew Dillon 
82434ebae70SMatthew Dillon 	if (ac != 2)
82534ebae70SMatthew Dillon 		mirror_usage(1);
82634ebae70SMatthew Dillon 
827e7f926a5SMatthew Dillon 	TwoWayPipeOpt = 1;
828*0bd7a37cSMatthew Dillon 	signal(SIGPIPE, SIG_IGN);
829e7f926a5SMatthew Dillon 
830e7f926a5SMatthew Dillon again:
83134ebae70SMatthew Dillon 	if (pipe(fds) < 0) {
83234ebae70SMatthew Dillon 		perror("pipe");
83334ebae70SMatthew Dillon 		exit(1);
83434ebae70SMatthew Dillon 	}
83534ebae70SMatthew Dillon 
83634ebae70SMatthew Dillon 	/*
83734ebae70SMatthew Dillon 	 * Source
83834ebae70SMatthew Dillon 	 */
83934ebae70SMatthew Dillon 	if ((pid1 = fork()) == 0) {
840*0bd7a37cSMatthew Dillon 		signal(SIGPIPE, SIG_DFL);
84134ebae70SMatthew Dillon 		dup2(fds[0], 0);
84234ebae70SMatthew Dillon 		dup2(fds[0], 1);
84334ebae70SMatthew Dillon 		close(fds[0]);
84434ebae70SMatthew Dillon 		close(fds[1]);
84534ebae70SMatthew Dillon 		if ((ptr = strchr(av[0], ':')) != NULL) {
84634ebae70SMatthew Dillon 			*ptr++ = 0;
847243ca327SMatthew Dillon 			xac = 0;
848243ca327SMatthew Dillon 			xav[xac++] = "ssh";
849243ca327SMatthew Dillon 			xav[xac++] = av[0];
850243ca327SMatthew Dillon 			xav[xac++] = "hammer";
85148eadef9SMatthew Dillon 
85248eadef9SMatthew Dillon 			switch(VerboseOpt) {
85348eadef9SMatthew Dillon 			case 0:
85448eadef9SMatthew Dillon 				break;
85548eadef9SMatthew Dillon 			case 1:
856243ca327SMatthew Dillon 				xav[xac++] = "-v";
85748eadef9SMatthew Dillon 				break;
85848eadef9SMatthew Dillon 			case 2:
85948eadef9SMatthew Dillon 				xav[xac++] = "-vv";
86048eadef9SMatthew Dillon 				break;
86148eadef9SMatthew Dillon 			default:
86248eadef9SMatthew Dillon 				xav[xac++] = "-vvv";
86348eadef9SMatthew Dillon 				break;
86448eadef9SMatthew Dillon 			}
86507485271SMichael Neumann 			if (ForceYesOpt) {
86607485271SMichael Neumann 				xav[xac++] = "-y";
86707485271SMichael Neumann 			}
868243ca327SMatthew Dillon 			xav[xac++] = "-2";
869243ca327SMatthew Dillon 			if (TimeoutOpt) {
870243ca327SMatthew Dillon 				snprintf(tbuf, sizeof(tbuf), "%d", TimeoutOpt);
871243ca327SMatthew Dillon 				xav[xac++] = "-t";
872243ca327SMatthew Dillon 				xav[xac++] = tbuf;
873243ca327SMatthew Dillon 			}
87448eadef9SMatthew Dillon 			if (streaming)
875901f434aSMatthew Dillon 				xav[xac++] = "mirror-read-stream";
87648eadef9SMatthew Dillon 			else
877243ca327SMatthew Dillon 				xav[xac++] = "mirror-read";
878243ca327SMatthew Dillon 			xav[xac++] = ptr;
879243ca327SMatthew Dillon 			xav[xac++] = NULL;
880243ca327SMatthew Dillon 			execv("/usr/bin/ssh", (void *)xav);
88134ebae70SMatthew Dillon 		} else {
88248eadef9SMatthew Dillon 			hammer_cmd_mirror_read(av, 1, streaming);
883243ca327SMatthew Dillon 			fflush(stdout);
884243ca327SMatthew Dillon 			fflush(stderr);
88534ebae70SMatthew Dillon 		}
88653d93cc7SMatthew Dillon 		_exit(1);
88734ebae70SMatthew Dillon 	}
88834ebae70SMatthew Dillon 
88934ebae70SMatthew Dillon 	/*
89034ebae70SMatthew Dillon 	 * Target
89134ebae70SMatthew Dillon 	 */
89234ebae70SMatthew Dillon 	if ((pid2 = fork()) == 0) {
893*0bd7a37cSMatthew Dillon 		signal(SIGPIPE, SIG_DFL);
89434ebae70SMatthew Dillon 		dup2(fds[1], 0);
89534ebae70SMatthew Dillon 		dup2(fds[1], 1);
89634ebae70SMatthew Dillon 		close(fds[0]);
89734ebae70SMatthew Dillon 		close(fds[1]);
89834ebae70SMatthew Dillon 		if ((ptr = strchr(av[1], ':')) != NULL) {
89934ebae70SMatthew Dillon 			*ptr++ = 0;
900243ca327SMatthew Dillon 			xac = 0;
901243ca327SMatthew Dillon 			xav[xac++] = "ssh";
902243ca327SMatthew Dillon 			xav[xac++] = av[1];
903243ca327SMatthew Dillon 			xav[xac++] = "hammer";
90448eadef9SMatthew Dillon 
90548eadef9SMatthew Dillon 			switch(VerboseOpt) {
90648eadef9SMatthew Dillon 			case 0:
90748eadef9SMatthew Dillon 				break;
90848eadef9SMatthew Dillon 			case 1:
909243ca327SMatthew Dillon 				xav[xac++] = "-v";
91048eadef9SMatthew Dillon 				break;
91148eadef9SMatthew Dillon 			case 2:
91248eadef9SMatthew Dillon 				xav[xac++] = "-vv";
91348eadef9SMatthew Dillon 				break;
91448eadef9SMatthew Dillon 			default:
91548eadef9SMatthew Dillon 				xav[xac++] = "-vvv";
91648eadef9SMatthew Dillon 				break;
91748eadef9SMatthew Dillon 			}
91807485271SMichael Neumann 			if (ForceYesOpt) {
91907485271SMichael Neumann 				xav[xac++] = "-y";
92007485271SMichael Neumann 			}
921243ca327SMatthew Dillon 			xav[xac++] = "-2";
922243ca327SMatthew Dillon 			xav[xac++] = "mirror-write";
923243ca327SMatthew Dillon 			xav[xac++] = ptr;
924243ca327SMatthew Dillon 			xav[xac++] = NULL;
925243ca327SMatthew Dillon 			execv("/usr/bin/ssh", (void *)xav);
92634ebae70SMatthew Dillon 		} else {
92734ebae70SMatthew Dillon 			hammer_cmd_mirror_write(av + 1, 1);
928243ca327SMatthew Dillon 			fflush(stdout);
929243ca327SMatthew Dillon 			fflush(stderr);
93034ebae70SMatthew Dillon 		}
93153d93cc7SMatthew Dillon 		_exit(1);
93234ebae70SMatthew Dillon 	}
93334ebae70SMatthew Dillon 	close(fds[0]);
93434ebae70SMatthew Dillon 	close(fds[1]);
93534ebae70SMatthew Dillon 
93634ebae70SMatthew Dillon 	while (waitpid(pid1, NULL, 0) <= 0)
93734ebae70SMatthew Dillon 		;
93834ebae70SMatthew Dillon 	while (waitpid(pid2, NULL, 0) <= 0)
93934ebae70SMatthew Dillon 		;
940e7f926a5SMatthew Dillon 
941e7f926a5SMatthew Dillon 	/*
942e7f926a5SMatthew Dillon 	 * If the link is lost restart
943e7f926a5SMatthew Dillon 	 */
944e7f926a5SMatthew Dillon 	if (streaming) {
945e7f926a5SMatthew Dillon 		if (VerboseOpt) {
946e7f926a5SMatthew Dillon 			fprintf(stderr, "\nLost Link\n");
947e7f926a5SMatthew Dillon 			fflush(stderr);
948e7f926a5SMatthew Dillon 		}
949*0bd7a37cSMatthew Dillon 		sleep(15 + DelayOpt);
950e7f926a5SMatthew Dillon 		goto again;
951e7f926a5SMatthew Dillon 	}
952e7f926a5SMatthew Dillon 
953a7fbbf91SMatthew Dillon }
954a7fbbf91SMatthew Dillon 
955243ca327SMatthew Dillon /*
956243ca327SMatthew Dillon  * Read and return multiple mrecords
957243ca327SMatthew Dillon  */
958a7fbbf91SMatthew Dillon static int
95917dd83bcSMatthew Dillon read_mrecords(int fd, char *buf, u_int size, hammer_ioc_mrecord_head_t pickup)
960a7fbbf91SMatthew Dillon {
96117dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
962a7fbbf91SMatthew Dillon 	u_int count;
963a7fbbf91SMatthew Dillon 	size_t n;
964a7fbbf91SMatthew Dillon 	size_t i;
96517dd83bcSMatthew Dillon 	size_t bytes;
96685a8e8a7SMatthew Dillon 	int type;
967a7fbbf91SMatthew Dillon 
968a7fbbf91SMatthew Dillon 	count = 0;
969a7fbbf91SMatthew Dillon 	while (size - count >= HAMMER_MREC_HEADSIZE) {
970a7fbbf91SMatthew Dillon 		/*
971a7fbbf91SMatthew Dillon 		 * Cached the record header in case we run out of buffer
972a7fbbf91SMatthew Dillon 		 * space.
973a7fbbf91SMatthew Dillon 		 */
97417dd83bcSMatthew Dillon 		fflush(stdout);
975a7fbbf91SMatthew Dillon 		if (pickup->signature == 0) {
976a7fbbf91SMatthew Dillon 			for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) {
977a7fbbf91SMatthew Dillon 				i = read(fd, (char *)pickup + n,
978a7fbbf91SMatthew Dillon 					 HAMMER_MREC_HEADSIZE - n);
979a7fbbf91SMatthew Dillon 				if (i <= 0)
980a7fbbf91SMatthew Dillon 					break;
981a7fbbf91SMatthew Dillon 			}
982a7fbbf91SMatthew Dillon 			if (n == 0)
983a7fbbf91SMatthew Dillon 				break;
984a7fbbf91SMatthew Dillon 			if (n != HAMMER_MREC_HEADSIZE) {
985a7fbbf91SMatthew Dillon 				fprintf(stderr, "read_mrecords: short read on pipe\n");
986a7fbbf91SMatthew Dillon 				exit(1);
987a7fbbf91SMatthew Dillon 			}
988a7fbbf91SMatthew Dillon 			if (pickup->signature != HAMMER_IOC_MIRROR_SIGNATURE) {
98934bb69d8SThomas Nikolajsen 				fprintf(stderr, "read_mrecords: malformed record on pipe, "
99034bb69d8SThomas Nikolajsen 					"bad signature\n");
991a7fbbf91SMatthew Dillon 				exit(1);
992a7fbbf91SMatthew Dillon 			}
993a7fbbf91SMatthew Dillon 		}
994a7fbbf91SMatthew Dillon 		if (pickup->rec_size < HAMMER_MREC_HEADSIZE ||
99517dd83bcSMatthew Dillon 		    pickup->rec_size > sizeof(*mrec) + HAMMER_XBUFSIZE) {
99634bb69d8SThomas Nikolajsen 			fprintf(stderr, "read_mrecords: malformed record on pipe, "
99734bb69d8SThomas Nikolajsen 				"illegal rec_size\n");
998a7fbbf91SMatthew Dillon 			exit(1);
999a7fbbf91SMatthew Dillon 		}
1000a7fbbf91SMatthew Dillon 
1001a7fbbf91SMatthew Dillon 		/*
1002a7fbbf91SMatthew Dillon 		 * Stop if we have insufficient space for the record and data.
1003a7fbbf91SMatthew Dillon 		 */
100417dd83bcSMatthew Dillon 		bytes = HAMMER_HEAD_DOALIGN(pickup->rec_size);
100517dd83bcSMatthew Dillon 		if (size - count < bytes)
1006a7fbbf91SMatthew Dillon 			break;
1007a7fbbf91SMatthew Dillon 
1008a7fbbf91SMatthew Dillon 		/*
100985a8e8a7SMatthew Dillon 		 * Stop if the record type is not a REC, SKIP, or PASS,
101085a8e8a7SMatthew Dillon 		 * which are the only types the ioctl supports.  Other types
101185a8e8a7SMatthew Dillon 		 * are used only by the userland protocol.
101285a8e8a7SMatthew Dillon 		 *
101385a8e8a7SMatthew Dillon 		 * Ignore all flags.
1014243ca327SMatthew Dillon 		 */
101585a8e8a7SMatthew Dillon 		type = pickup->type & HAMMER_MRECF_TYPE_LOMASK;
101685a8e8a7SMatthew Dillon 		if (type != HAMMER_MREC_TYPE_PFSD &&
101785a8e8a7SMatthew Dillon 		    type != HAMMER_MREC_TYPE_REC &&
101885a8e8a7SMatthew Dillon 		    type != HAMMER_MREC_TYPE_SKIP &&
101985a8e8a7SMatthew Dillon 		    type != HAMMER_MREC_TYPE_PASS) {
1020243ca327SMatthew Dillon 			break;
102117dd83bcSMatthew Dillon 		}
1022243ca327SMatthew Dillon 
1023243ca327SMatthew Dillon 		/*
1024a7fbbf91SMatthew Dillon 		 * Read the remainder and clear the pickup signature.
1025a7fbbf91SMatthew Dillon 		 */
102617dd83bcSMatthew Dillon 		for (n = HAMMER_MREC_HEADSIZE; n < bytes; n += i) {
102717dd83bcSMatthew Dillon 			i = read(fd, buf + count + n, bytes - n);
1028a7fbbf91SMatthew Dillon 			if (i <= 0)
1029a7fbbf91SMatthew Dillon 				break;
1030a7fbbf91SMatthew Dillon 		}
103117dd83bcSMatthew Dillon 		if (n != bytes) {
1032a7fbbf91SMatthew Dillon 			fprintf(stderr, "read_mrecords: short read on pipe\n");
1033a7fbbf91SMatthew Dillon 			exit(1);
1034a7fbbf91SMatthew Dillon 		}
103517dd83bcSMatthew Dillon 
103617dd83bcSMatthew Dillon 		bcopy(pickup, buf + count, HAMMER_MREC_HEADSIZE);
103717dd83bcSMatthew Dillon 		pickup->signature = 0;
103817dd83bcSMatthew Dillon 		pickup->type = 0;
103917dd83bcSMatthew Dillon 		mrec = (void *)(buf + count);
104017dd83bcSMatthew Dillon 
104117dd83bcSMatthew Dillon 		/*
104217dd83bcSMatthew Dillon 		 * Validate the completed record
104317dd83bcSMatthew Dillon 		 */
104417dd83bcSMatthew Dillon 		if (mrec->head.rec_crc !=
104517dd83bcSMatthew Dillon 		    crc32((char *)mrec + HAMMER_MREC_CRCOFF,
104617dd83bcSMatthew Dillon 			  mrec->head.rec_size - HAMMER_MREC_CRCOFF)) {
104717dd83bcSMatthew Dillon 			fprintf(stderr, "read_mrecords: malformed record "
104817dd83bcSMatthew Dillon 					"on pipe, bad crc\n");
104917dd83bcSMatthew Dillon 			exit(1);
1050a7fbbf91SMatthew Dillon 		}
1051a7fbbf91SMatthew Dillon 
105217dd83bcSMatthew Dillon 		/*
105385a8e8a7SMatthew Dillon 		 * If its a B-Tree record validate the data crc.
105485a8e8a7SMatthew Dillon 		 *
105585a8e8a7SMatthew Dillon 		 * NOTE: If the VFS passes us an explicitly errorde mrec
105685a8e8a7SMatthew Dillon 		 *	 we just pass it through.
105717dd83bcSMatthew Dillon 		 */
105885a8e8a7SMatthew Dillon 		type = mrec->head.type & HAMMER_MRECF_TYPE_MASK;
105985a8e8a7SMatthew Dillon 
106085a8e8a7SMatthew Dillon 		if (type == HAMMER_MREC_TYPE_REC) {
106117dd83bcSMatthew Dillon 			if (mrec->head.rec_size <
106217dd83bcSMatthew Dillon 			    sizeof(mrec->rec) + mrec->rec.leaf.data_len) {
106317dd83bcSMatthew Dillon 				fprintf(stderr,
106417dd83bcSMatthew Dillon 					"read_mrecords: malformed record on "
106517dd83bcSMatthew Dillon 					"pipe, illegal element data_len\n");
106617dd83bcSMatthew Dillon 				exit(1);
106717dd83bcSMatthew Dillon 			}
106817dd83bcSMatthew Dillon 			if (mrec->rec.leaf.data_len &&
106917dd83bcSMatthew Dillon 			    mrec->rec.leaf.data_offset &&
107017dd83bcSMatthew Dillon 			    hammer_crc_test_leaf(&mrec->rec + 1, &mrec->rec.leaf) == 0) {
107117dd83bcSMatthew Dillon 				fprintf(stderr,
107217dd83bcSMatthew Dillon 					"read_mrecords: data_crc did not "
107317dd83bcSMatthew Dillon 					"match data! obj=%016llx key=%016llx\n",
107417dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_id,
107517dd83bcSMatthew Dillon 					mrec->rec.leaf.base.key);
107617dd83bcSMatthew Dillon 				fprintf(stderr,
107717dd83bcSMatthew Dillon 					"continuing, but there are problems\n");
107817dd83bcSMatthew Dillon 			}
107917dd83bcSMatthew Dillon 		}
108017dd83bcSMatthew Dillon 		count += bytes;
1081a7fbbf91SMatthew Dillon 	}
1082a7fbbf91SMatthew Dillon 	return(count);
1083a7fbbf91SMatthew Dillon }
1084a7fbbf91SMatthew Dillon 
108534ebae70SMatthew Dillon /*
108617dd83bcSMatthew Dillon  * Read and return a single mrecord.
1087243ca327SMatthew Dillon  */
1088243ca327SMatthew Dillon static
108917dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t
109017dd83bcSMatthew Dillon read_mrecord(int fdin, int *errorp, hammer_ioc_mrecord_head_t pickup)
1091243ca327SMatthew Dillon {
109217dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
109317dd83bcSMatthew Dillon 	struct hammer_ioc_mrecord_head mrechd;
1094243ca327SMatthew Dillon 	size_t bytes;
1095243ca327SMatthew Dillon 	size_t n;
1096243ca327SMatthew Dillon 	size_t i;
1097243ca327SMatthew Dillon 
1098243ca327SMatthew Dillon 	if (pickup && pickup->type != 0) {
1099243ca327SMatthew Dillon 		mrechd = *pickup;
1100243ca327SMatthew Dillon 		pickup->signature = 0;
1101243ca327SMatthew Dillon 		pickup->type = 0;
1102243ca327SMatthew Dillon 		n = HAMMER_MREC_HEADSIZE;
1103243ca327SMatthew Dillon 	} else {
1104243ca327SMatthew Dillon 		/*
1105243ca327SMatthew Dillon 		 * Read in the PFSD header from the sender.
1106243ca327SMatthew Dillon 		 */
1107243ca327SMatthew Dillon 		for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) {
1108243ca327SMatthew Dillon 			i = read(fdin, (char *)&mrechd + n, HAMMER_MREC_HEADSIZE - n);
1109243ca327SMatthew Dillon 			if (i <= 0)
1110243ca327SMatthew Dillon 				break;
1111243ca327SMatthew Dillon 		}
1112243ca327SMatthew Dillon 		if (n == 0) {
1113243ca327SMatthew Dillon 			*errorp = 0;	/* EOF */
1114243ca327SMatthew Dillon 			return(NULL);
1115243ca327SMatthew Dillon 		}
1116243ca327SMatthew Dillon 		if (n != HAMMER_MREC_HEADSIZE) {
1117243ca327SMatthew Dillon 			fprintf(stderr, "short read of mrecord header\n");
1118243ca327SMatthew Dillon 			*errorp = EPIPE;
1119243ca327SMatthew Dillon 			return(NULL);
1120243ca327SMatthew Dillon 		}
1121243ca327SMatthew Dillon 	}
1122243ca327SMatthew Dillon 	if (mrechd.signature != HAMMER_IOC_MIRROR_SIGNATURE) {
1123243ca327SMatthew Dillon 		fprintf(stderr, "read_mrecord: bad signature\n");
1124243ca327SMatthew Dillon 		*errorp = EINVAL;
1125243ca327SMatthew Dillon 		return(NULL);
1126243ca327SMatthew Dillon 	}
112717dd83bcSMatthew Dillon 	bytes = HAMMER_HEAD_DOALIGN(mrechd.rec_size);
112817dd83bcSMatthew Dillon 	assert(bytes >= sizeof(mrechd));
1129243ca327SMatthew Dillon 	mrec = malloc(bytes);
113017dd83bcSMatthew Dillon 	mrec->head = mrechd;
113117dd83bcSMatthew Dillon 
1132243ca327SMatthew Dillon 	while (n < bytes) {
1133243ca327SMatthew Dillon 		i = read(fdin, (char *)mrec + n, bytes - n);
1134243ca327SMatthew Dillon 		if (i <= 0)
1135243ca327SMatthew Dillon 			break;
1136243ca327SMatthew Dillon 		n += i;
1137243ca327SMatthew Dillon 	}
1138243ca327SMatthew Dillon 	if (n != bytes) {
1139243ca327SMatthew Dillon 		fprintf(stderr, "read_mrecord: short read on payload\n");
1140243ca327SMatthew Dillon 		*errorp = EPIPE;
1141243ca327SMatthew Dillon 		return(NULL);
1142243ca327SMatthew Dillon 	}
114317dd83bcSMatthew Dillon 	if (mrec->head.rec_crc !=
114417dd83bcSMatthew Dillon 	    crc32((char *)mrec + HAMMER_MREC_CRCOFF,
114517dd83bcSMatthew Dillon 		  mrec->head.rec_size - HAMMER_MREC_CRCOFF)) {
1146243ca327SMatthew Dillon 		fprintf(stderr, "read_mrecord: bad CRC\n");
1147243ca327SMatthew Dillon 		*errorp = EINVAL;
1148243ca327SMatthew Dillon 		return(NULL);
1149243ca327SMatthew Dillon 	}
1150243ca327SMatthew Dillon 	*errorp = 0;
1151243ca327SMatthew Dillon 	return(mrec);
1152243ca327SMatthew Dillon }
1153243ca327SMatthew Dillon 
1154243ca327SMatthew Dillon static
1155243ca327SMatthew Dillon void
115617dd83bcSMatthew Dillon write_mrecord(int fdout, u_int32_t type, hammer_ioc_mrecord_any_t mrec,
115717dd83bcSMatthew Dillon 	      int bytes)
1158243ca327SMatthew Dillon {
115917dd83bcSMatthew Dillon 	char zbuf[HAMMER_HEAD_ALIGN];
116017dd83bcSMatthew Dillon 	int pad;
1161243ca327SMatthew Dillon 
116217dd83bcSMatthew Dillon 	pad = HAMMER_HEAD_DOALIGN(bytes) - bytes;
116317dd83bcSMatthew Dillon 
116417dd83bcSMatthew Dillon 	assert(bytes >= (int)sizeof(mrec->head));
116517dd83bcSMatthew Dillon 	bzero(&mrec->head, sizeof(mrec->head));
116617dd83bcSMatthew Dillon 	mrec->head.signature = HAMMER_IOC_MIRROR_SIGNATURE;
116717dd83bcSMatthew Dillon 	mrec->head.type = type;
116817dd83bcSMatthew Dillon 	mrec->head.rec_size = bytes;
116917dd83bcSMatthew Dillon 	mrec->head.rec_crc = crc32((char *)mrec + HAMMER_MREC_CRCOFF,
117017dd83bcSMatthew Dillon 				   bytes - HAMMER_MREC_CRCOFF);
117117dd83bcSMatthew Dillon 	if (write(fdout, mrec, bytes) != bytes) {
1172243ca327SMatthew Dillon 		fprintf(stderr, "write_mrecord: error %d (%s)\n",
1173243ca327SMatthew Dillon 			errno, strerror(errno));
1174243ca327SMatthew Dillon 		exit(1);
1175243ca327SMatthew Dillon 	}
117617dd83bcSMatthew Dillon 	if (pad) {
117717dd83bcSMatthew Dillon 		bzero(zbuf, pad);
117817dd83bcSMatthew Dillon 		if (write(fdout, zbuf, pad) != pad) {
117917dd83bcSMatthew Dillon 			fprintf(stderr, "write_mrecord: error %d (%s)\n",
118017dd83bcSMatthew Dillon 				errno, strerror(errno));
118117dd83bcSMatthew Dillon 			exit(1);
118217dd83bcSMatthew Dillon 		}
118317dd83bcSMatthew Dillon 	}
1184243ca327SMatthew Dillon }
1185243ca327SMatthew Dillon 
1186243ca327SMatthew Dillon /*
118734ebae70SMatthew Dillon  * Generate a mirroring header with the pfs information of the
118834ebae70SMatthew Dillon  * originating filesytem.
118934ebae70SMatthew Dillon  */
119034ebae70SMatthew Dillon static void
1191e7f926a5SMatthew Dillon generate_mrec_header(int fd, int pfs_id,
1192e7f926a5SMatthew Dillon 		     union hammer_ioc_mrecord_any *mrec_tmp)
119334ebae70SMatthew Dillon {
119434ebae70SMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
119534ebae70SMatthew Dillon 
119634ebae70SMatthew Dillon 	bzero(&pfs, sizeof(pfs));
1197e7f926a5SMatthew Dillon 	bzero(mrec_tmp, sizeof(*mrec_tmp));
1198d4e5b69bSMatthew Dillon 	pfs.pfs_id = pfs_id;
1199e7f926a5SMatthew Dillon 	pfs.ondisk = &mrec_tmp->pfs.pfsd;
1200e7f926a5SMatthew Dillon 	pfs.bytes = sizeof(mrec_tmp->pfs.pfsd);
120134ebae70SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
12029dc76cb1SMatthew Dillon 		fprintf(stderr, "Mirror-read: not a HAMMER fs/pseudofs!\n");
120334ebae70SMatthew Dillon 		exit(1);
120434ebae70SMatthew Dillon 	}
120534ebae70SMatthew Dillon 	if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
12069dc76cb1SMatthew Dillon 		fprintf(stderr, "Mirror-read: HAMMER pfs version mismatch!\n");
120734ebae70SMatthew Dillon 		exit(1);
120834ebae70SMatthew Dillon 	}
1209e7f926a5SMatthew Dillon 	mrec_tmp->pfs.version = pfs.version;
121034ebae70SMatthew Dillon }
121134ebae70SMatthew Dillon 
121234ebae70SMatthew Dillon /*
121334ebae70SMatthew Dillon  * Validate the pfs information from the originating filesystem
121434ebae70SMatthew Dillon  * against the target filesystem.  shared_uuid must match.
121548eadef9SMatthew Dillon  *
121648eadef9SMatthew Dillon  * return -1 if we got a TERM record
121734ebae70SMatthew Dillon  */
121848eadef9SMatthew Dillon static int
1219d4e5b69bSMatthew Dillon validate_mrec_header(int fd, int fdin, int is_target, int pfs_id,
122048eadef9SMatthew Dillon 		     struct hammer_ioc_mrecord_head *pickup,
122134ebae70SMatthew Dillon 		     hammer_tid_t *tid_begp, hammer_tid_t *tid_endp)
122234ebae70SMatthew Dillon {
122334ebae70SMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
122434ebae70SMatthew Dillon 	struct hammer_pseudofs_data pfsd;
122517dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
1226243ca327SMatthew Dillon 	int error;
122734ebae70SMatthew Dillon 
122834ebae70SMatthew Dillon 	/*
122934ebae70SMatthew Dillon 	 * Get the PFSD info from the target filesystem.
123034ebae70SMatthew Dillon 	 */
123134ebae70SMatthew Dillon 	bzero(&pfs, sizeof(pfs));
123234ebae70SMatthew Dillon 	bzero(&pfsd, sizeof(pfsd));
1233d4e5b69bSMatthew Dillon 	pfs.pfs_id = pfs_id;
123434ebae70SMatthew Dillon 	pfs.ondisk = &pfsd;
123534ebae70SMatthew Dillon 	pfs.bytes = sizeof(pfsd);
123634ebae70SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
123734ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: not a HAMMER fs/pseudofs!\n");
123834ebae70SMatthew Dillon 		exit(1);
123934ebae70SMatthew Dillon 	}
124034ebae70SMatthew Dillon 	if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
124134ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: HAMMER pfs version mismatch!\n");
124234ebae70SMatthew Dillon 		exit(1);
124334ebae70SMatthew Dillon 	}
124434ebae70SMatthew Dillon 
124548eadef9SMatthew Dillon 	mrec = read_mrecord(fdin, &error, pickup);
1246243ca327SMatthew Dillon 	if (mrec == NULL) {
1247243ca327SMatthew Dillon 		if (error == 0)
1248243ca327SMatthew Dillon 			fprintf(stderr, "validate_mrec_header: short read\n");
124934ebae70SMatthew Dillon 		exit(1);
125034ebae70SMatthew Dillon 	}
125148eadef9SMatthew Dillon 	if (mrec->head.type == HAMMER_MREC_TYPE_TERM) {
125248eadef9SMatthew Dillon 		free(mrec);
125348eadef9SMatthew Dillon 		return(-1);
125448eadef9SMatthew Dillon 	}
125548eadef9SMatthew Dillon 
125617dd83bcSMatthew Dillon 	if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) {
1257243ca327SMatthew Dillon 		fprintf(stderr, "validate_mrec_header: did not get expected "
1258243ca327SMatthew Dillon 				"PFSD record type\n");
125934ebae70SMatthew Dillon 		exit(1);
126034ebae70SMatthew Dillon 	}
126117dd83bcSMatthew Dillon 	if (mrec->head.rec_size != sizeof(mrec->pfs)) {
1262243ca327SMatthew Dillon 		fprintf(stderr, "validate_mrec_header: unexpected payload "
1263243ca327SMatthew Dillon 				"size\n");
126434ebae70SMatthew Dillon 		exit(1);
126534ebae70SMatthew Dillon 	}
126617dd83bcSMatthew Dillon 	if (mrec->pfs.version != pfs.version) {
1267243ca327SMatthew Dillon 		fprintf(stderr, "validate_mrec_header: Version mismatch\n");
126834ebae70SMatthew Dillon 		exit(1);
126934ebae70SMatthew Dillon 	}
127034ebae70SMatthew Dillon 
127134ebae70SMatthew Dillon 	/*
127234ebae70SMatthew Dillon 	 * Whew.  Ok, is the read PFS info compatible with the target?
127334ebae70SMatthew Dillon 	 */
127417dd83bcSMatthew Dillon 	if (bcmp(&mrec->pfs.pfsd.shared_uuid, &pfsd.shared_uuid,
127517dd83bcSMatthew Dillon 		 sizeof(pfsd.shared_uuid)) != 0) {
127617dd83bcSMatthew Dillon 		fprintf(stderr,
127717dd83bcSMatthew Dillon 			"mirror-write: source and target have "
1278f265b84fSSascha Wildner 			"different shared-uuid's!\n");
127934ebae70SMatthew Dillon 		exit(1);
128034ebae70SMatthew Dillon 	}
1281d4e5b69bSMatthew Dillon 	if (is_target &&
1282d4e5b69bSMatthew Dillon 	    (pfsd.mirror_flags & HAMMER_PFSD_SLAVE) == 0) {
128334ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: target must be in slave mode\n");
128434ebae70SMatthew Dillon 		exit(1);
128534ebae70SMatthew Dillon 	}
1286d4e5b69bSMatthew Dillon 	if (tid_begp)
128717dd83bcSMatthew Dillon 		*tid_begp = mrec->pfs.pfsd.sync_beg_tid;
1288d4e5b69bSMatthew Dillon 	if (tid_endp)
128917dd83bcSMatthew Dillon 		*tid_endp = mrec->pfs.pfsd.sync_end_tid;
1290243ca327SMatthew Dillon 	free(mrec);
129148eadef9SMatthew Dillon 	return(0);
129234ebae70SMatthew Dillon }
129334ebae70SMatthew Dillon 
129434ebae70SMatthew Dillon static void
1295d4e5b69bSMatthew Dillon update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id)
129634ebae70SMatthew Dillon {
1297243ca327SMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
1298243ca327SMatthew Dillon 	struct hammer_pseudofs_data pfsd;
129934ebae70SMatthew Dillon 
1300243ca327SMatthew Dillon 	bzero(&pfs, sizeof(pfs));
1301243ca327SMatthew Dillon 	bzero(&pfsd, sizeof(pfsd));
1302d4e5b69bSMatthew Dillon 	pfs.pfs_id = pfs_id;
1303243ca327SMatthew Dillon 	pfs.ondisk = &pfsd;
1304243ca327SMatthew Dillon 	pfs.bytes = sizeof(pfsd);
1305243ca327SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
1306243ca327SMatthew Dillon 		perror("update_pfs_snapshot (read)");
1307243ca327SMatthew Dillon 		exit(1);
130834ebae70SMatthew Dillon 	}
13099c67b4d2SMatthew Dillon 	if (pfsd.sync_end_tid != snapshot_tid) {
1310ddc8e722SMatthew Dillon 		pfsd.sync_end_tid = snapshot_tid;
1311243ca327SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) != 0) {
1312243ca327SMatthew Dillon 			perror("update_pfs_snapshot (rewrite)");
1313243ca327SMatthew Dillon 			exit(1);
131434ebae70SMatthew Dillon 		}
131548eadef9SMatthew Dillon 		if (VerboseOpt >= 2) {
13169dc76cb1SMatthew Dillon 			fprintf(stderr,
13179dc76cb1SMatthew Dillon 				"Mirror-write: Completed, updated snapshot "
13189dc76cb1SMatthew Dillon 				"to %016llx\n",
13199dc76cb1SMatthew Dillon 				snapshot_tid);
1320243ca327SMatthew Dillon 		}
13219c67b4d2SMatthew Dillon 	}
132248eadef9SMatthew Dillon }
1323243ca327SMatthew Dillon 
132448eadef9SMatthew Dillon /*
132548eadef9SMatthew Dillon  * Bandwidth-limited write in chunks
132648eadef9SMatthew Dillon  */
132748eadef9SMatthew Dillon static
132848eadef9SMatthew Dillon ssize_t
132948eadef9SMatthew Dillon writebw(int fd, const void *buf, size_t nbytes,
133048eadef9SMatthew Dillon 	u_int64_t *bwcount, struct timeval *tv1)
133148eadef9SMatthew Dillon {
133248eadef9SMatthew Dillon 	struct timeval tv2;
133348eadef9SMatthew Dillon 	size_t n;
133448eadef9SMatthew Dillon 	ssize_t r;
133548eadef9SMatthew Dillon 	ssize_t a;
133648eadef9SMatthew Dillon 	int usec;
133748eadef9SMatthew Dillon 
133848eadef9SMatthew Dillon 	a = 0;
133948eadef9SMatthew Dillon 	r = 0;
134048eadef9SMatthew Dillon 	while (nbytes) {
134148eadef9SMatthew Dillon 		if (*bwcount + nbytes > BandwidthOpt)
134248eadef9SMatthew Dillon 			n = BandwidthOpt - *bwcount;
134348eadef9SMatthew Dillon 		else
134448eadef9SMatthew Dillon 			n = nbytes;
134548eadef9SMatthew Dillon 		if (n)
134648eadef9SMatthew Dillon 			r = write(fd, buf, n);
134748eadef9SMatthew Dillon 		if (r >= 0) {
134848eadef9SMatthew Dillon 			a += r;
134948eadef9SMatthew Dillon 			nbytes -= r;
135048eadef9SMatthew Dillon 			buf = (const char *)buf + r;
135148eadef9SMatthew Dillon 		}
135248eadef9SMatthew Dillon 		if ((size_t)r != n)
135348eadef9SMatthew Dillon 			break;
135448eadef9SMatthew Dillon 		*bwcount += n;
135548eadef9SMatthew Dillon 		if (*bwcount >= BandwidthOpt) {
135648eadef9SMatthew Dillon 			gettimeofday(&tv2, NULL);
135748eadef9SMatthew Dillon 			usec = (int)(tv2.tv_sec - tv1->tv_sec) * 1000000 +
135848eadef9SMatthew Dillon 				(int)(tv2.tv_usec - tv1->tv_usec);
135948eadef9SMatthew Dillon 			if (usec >= 0 && usec < 1000000)
136048eadef9SMatthew Dillon 				usleep(1000000 - usec);
136148eadef9SMatthew Dillon 			gettimeofday(tv1, NULL);
136248eadef9SMatthew Dillon 			*bwcount -= BandwidthOpt;
136348eadef9SMatthew Dillon 		}
136448eadef9SMatthew Dillon 	}
136548eadef9SMatthew Dillon 	return(a ? a : r);
136648eadef9SMatthew Dillon }
136734ebae70SMatthew Dillon 
136801a72c9fSMatthew Dillon /*
136901a72c9fSMatthew Dillon  * Get a yes or no answer from the terminal.  The program may be run as
137001a72c9fSMatthew Dillon  * part of a two-way pipe so we cannot use stdin for this operation.
137101a72c9fSMatthew Dillon  */
137201a72c9fSMatthew Dillon static int
137301a72c9fSMatthew Dillon getyn(void)
137401a72c9fSMatthew Dillon {
137501a72c9fSMatthew Dillon 	char buf[256];
137601a72c9fSMatthew Dillon 	FILE *fp;
137701a72c9fSMatthew Dillon 	int result;
137801a72c9fSMatthew Dillon 
137901a72c9fSMatthew Dillon 	fp = fopen("/dev/tty", "r");
138001a72c9fSMatthew Dillon 	if (fp == NULL) {
138101a72c9fSMatthew Dillon 		fprintf(stderr, "No terminal for response\n");
138201a72c9fSMatthew Dillon 		return(-1);
138301a72c9fSMatthew Dillon 	}
138401a72c9fSMatthew Dillon 	result = -1;
138501a72c9fSMatthew Dillon 	while (fgets(buf, sizeof(buf), fp) != NULL) {
138601a72c9fSMatthew Dillon 		if (buf[0] == 'y' || buf[0] == 'Y') {
138701a72c9fSMatthew Dillon 			result = 1;
138801a72c9fSMatthew Dillon 			break;
138901a72c9fSMatthew Dillon 		}
139001a72c9fSMatthew Dillon 		if (buf[0] == 'n' || buf[0] == 'N') {
139101a72c9fSMatthew Dillon 			result = 0;
139201a72c9fSMatthew Dillon 			break;
139301a72c9fSMatthew Dillon 		}
139401a72c9fSMatthew Dillon 		fprintf(stderr, "Response not understood\n");
139501a72c9fSMatthew Dillon 		break;
139601a72c9fSMatthew Dillon 	}
139701a72c9fSMatthew Dillon 	fclose(fp);
139801a72c9fSMatthew Dillon 	return(result);
139901a72c9fSMatthew Dillon }
140001a72c9fSMatthew Dillon 
1401a7fbbf91SMatthew Dillon static void
1402a7fbbf91SMatthew Dillon mirror_usage(int code)
1403a7fbbf91SMatthew Dillon {
1404a7fbbf91SMatthew Dillon 	fprintf(stderr,
140534bb69d8SThomas Nikolajsen 		"hammer mirror-read <filesystem> [begin-tid]\n"
140634bb69d8SThomas Nikolajsen 		"hammer mirror-read-stream <filesystem> [begin-tid]\n"
1407a7fbbf91SMatthew Dillon 		"hammer mirror-write <filesystem>\n"
1408243ca327SMatthew Dillon 		"hammer mirror-dump\n"
140934bb69d8SThomas Nikolajsen 		"hammer mirror-copy [[user@]host:]<filesystem>"
141034bb69d8SThomas Nikolajsen 				  " [[user@]host:]<filesystem>\n"
141134bb69d8SThomas Nikolajsen 		"hammer mirror-stream [[user@]host:]<filesystem>"
141234bb69d8SThomas Nikolajsen 				    " [[user@]host:]<filesystem>\n"
1413a7fbbf91SMatthew Dillon 	);
1414a7fbbf91SMatthew Dillon 	exit(code);
1415a7fbbf91SMatthew Dillon }
141601a72c9fSMatthew Dillon 
1417