xref: /dflybsd-src/sbin/hammer/cmd_mirror.c (revision 48eadef9799e5b31eb2e3a02f581b2be633ad598)
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  *
34*48eadef9SMatthew Dillon  * $DragonFly: src/sbin/hammer/cmd_mirror.c,v 1.12 2008/07/31 06:01:31 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);
4317dd83bcSMatthew Dillon static hammer_ioc_mrecord_any_t read_mrecord(int fdin, int *errorp,
4417dd83bcSMatthew Dillon 			 hammer_ioc_mrecord_head_t pickup);
4517dd83bcSMatthew Dillon static void write_mrecord(int fdout, u_int32_t type,
4617dd83bcSMatthew Dillon 			 hammer_ioc_mrecord_any_t mrec, int bytes);
47d4e5b69bSMatthew Dillon static void generate_mrec_header(int fd, int fdout, int pfs_id,
4834ebae70SMatthew Dillon 			 hammer_tid_t *tid_begp, hammer_tid_t *tid_endp);
49*48eadef9SMatthew Dillon static int validate_mrec_header(int fd, int fdin, int is_target, int pfs_id,
50*48eadef9SMatthew Dillon 			 struct hammer_ioc_mrecord_head *pickup,
5134ebae70SMatthew Dillon 			 hammer_tid_t *tid_begp, hammer_tid_t *tid_endp);
52d4e5b69bSMatthew Dillon static void update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id);
53*48eadef9SMatthew Dillon static ssize_t writebw(int fd, const void *buf, size_t nbytes,
54*48eadef9SMatthew Dillon 			u_int64_t *bwcount, struct timeval *tv1);
55a7fbbf91SMatthew Dillon static void mirror_usage(int code);
56a7fbbf91SMatthew Dillon 
5717dd83bcSMatthew Dillon /*
5817dd83bcSMatthew Dillon  * Generate a mirroring data stream from the specific source over the
5917dd83bcSMatthew Dillon  * entire key range, but restricted to the specified transaction range.
6017dd83bcSMatthew Dillon  *
6117dd83bcSMatthew Dillon  * The HAMMER VFS does most of the work, we add a few new mrecord
6217dd83bcSMatthew Dillon  * types to negotiate the TID ranges and verify that the entire
6317dd83bcSMatthew Dillon  * stream made it to the destination.
6417dd83bcSMatthew Dillon  */
65a7fbbf91SMatthew Dillon void
66*48eadef9SMatthew Dillon hammer_cmd_mirror_read(char **av, int ac, int streaming)
67a7fbbf91SMatthew Dillon {
68a7fbbf91SMatthew Dillon 	struct hammer_ioc_mirror_rw mirror;
69d4e5b69bSMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
7017dd83bcSMatthew Dillon 	union hammer_ioc_mrecord_any mrec_tmp;
71*48eadef9SMatthew Dillon 	struct hammer_ioc_mrecord_head pickup;
7217dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
73243ca327SMatthew Dillon 	hammer_tid_t sync_tid;
74a7fbbf91SMatthew Dillon 	const char *filesystem;
75a7fbbf91SMatthew Dillon 	char *buf = malloc(SERIALBUF_SIZE);
76243ca327SMatthew Dillon 	int interrupted = 0;
77243ca327SMatthew Dillon 	int error;
78a7fbbf91SMatthew Dillon 	int fd;
79243ca327SMatthew Dillon 	int n;
80*48eadef9SMatthew Dillon 	int didwork;
81*48eadef9SMatthew Dillon 	int64_t total_bytes;
82243ca327SMatthew Dillon 	time_t base_t = time(NULL);
83*48eadef9SMatthew Dillon 	struct timeval bwtv;
84*48eadef9SMatthew Dillon 	u_int64_t bwcount;
85a7fbbf91SMatthew Dillon 
86a7fbbf91SMatthew Dillon 	if (ac > 2)
87a7fbbf91SMatthew Dillon 		mirror_usage(1);
88a7fbbf91SMatthew Dillon 	filesystem = av[0];
89a7fbbf91SMatthew Dillon 
90*48eadef9SMatthew Dillon 	pickup.signature = 0;
91*48eadef9SMatthew Dillon 	pickup.type = 0;
92*48eadef9SMatthew Dillon 
93*48eadef9SMatthew Dillon again:
94a7fbbf91SMatthew Dillon 	bzero(&mirror, sizeof(mirror));
95a7fbbf91SMatthew Dillon 	hammer_key_beg_init(&mirror.key_beg);
96a7fbbf91SMatthew Dillon 	hammer_key_end_init(&mirror.key_end);
97a7fbbf91SMatthew Dillon 
98d4e5b69bSMatthew Dillon 	fd = getpfs(&pfs, filesystem);
99a7fbbf91SMatthew Dillon 
100*48eadef9SMatthew Dillon 	if (streaming && VerboseOpt) {
101*48eadef9SMatthew Dillon 		fprintf(stderr, "\nRunning");
102*48eadef9SMatthew Dillon 		fflush(stderr);
103*48eadef9SMatthew Dillon 	}
104*48eadef9SMatthew Dillon 	total_bytes = 0;
105*48eadef9SMatthew Dillon 	gettimeofday(&bwtv, NULL);
106*48eadef9SMatthew Dillon 	bwcount = 0;
107*48eadef9SMatthew Dillon 
108243ca327SMatthew Dillon 	/*
109d4e5b69bSMatthew Dillon 	 * In 2-way mode the target will send us a PFS info packet
110d4e5b69bSMatthew Dillon 	 * first.  Use the target's current snapshot TID as our default
111d4e5b69bSMatthew Dillon 	 * begin TID.
112243ca327SMatthew Dillon 	 */
113d4e5b69bSMatthew Dillon 	mirror.tid_beg = 0;
114*48eadef9SMatthew Dillon 	if (TwoWayPipeOpt) {
115*48eadef9SMatthew Dillon 		n = validate_mrec_header(fd, 0, 0, pfs.pfs_id, &pickup,
116d4e5b69bSMatthew Dillon 					 NULL, &mirror.tid_beg);
117*48eadef9SMatthew Dillon 		if (n < 0) {	/* got TERM record */
118*48eadef9SMatthew Dillon 			relpfs(fd, &pfs);
119*48eadef9SMatthew Dillon 			return;
120*48eadef9SMatthew Dillon 		}
121*48eadef9SMatthew Dillon 		++mirror.tid_beg;
122*48eadef9SMatthew Dillon 	}
123d4e5b69bSMatthew Dillon 
124d4e5b69bSMatthew Dillon 	/*
125d4e5b69bSMatthew Dillon 	 * Write out the PFS header, tid_beg will be updated if our PFS
126d4e5b69bSMatthew Dillon 	 * has a larger begin sync.  tid_end is set to the latest source
127d4e5b69bSMatthew Dillon 	 * TID whos flush cycle has completed.
128d4e5b69bSMatthew Dillon 	 */
129d4e5b69bSMatthew Dillon 	generate_mrec_header(fd, 1, pfs.pfs_id,
130d4e5b69bSMatthew Dillon 			     &mirror.tid_beg, &mirror.tid_end);
131d4e5b69bSMatthew Dillon 
132*48eadef9SMatthew Dillon 	/* XXX streaming mode support w/ cycle or command line arg */
133d4e5b69bSMatthew Dillon 	/*
134d4e5b69bSMatthew Dillon 	 * A cycle file overrides the beginning TID
135d4e5b69bSMatthew Dillon 	 */
136243ca327SMatthew Dillon 	hammer_get_cycle(&mirror.key_beg, &mirror.tid_beg);
13734ebae70SMatthew Dillon 
13817dd83bcSMatthew Dillon 	if (ac == 2)
13917dd83bcSMatthew Dillon 		mirror.tid_beg = strtoull(av[1], NULL, 0);
14017dd83bcSMatthew Dillon 
141*48eadef9SMatthew Dillon 	if (streaming == 0 || VerboseOpt >= 2) {
142*48eadef9SMatthew Dillon 		fprintf(stderr,
143*48eadef9SMatthew Dillon 			"Mirror-read: Mirror from %016llx to %016llx\n",
144243ca327SMatthew Dillon 			mirror.tid_beg, mirror.tid_end);
145*48eadef9SMatthew Dillon 	}
146243ca327SMatthew Dillon 	if (mirror.key_beg.obj_id != (int64_t)HAMMER_MIN_OBJID) {
1479dc76cb1SMatthew Dillon 		fprintf(stderr, "Mirror-read: Resuming at object %016llx\n",
148243ca327SMatthew Dillon 			mirror.key_beg.obj_id);
149243ca327SMatthew Dillon 	}
150243ca327SMatthew Dillon 
151243ca327SMatthew Dillon 	/*
1529dc76cb1SMatthew Dillon 	 * Nothing to do if begin equals end.
1539dc76cb1SMatthew Dillon 	 */
154*48eadef9SMatthew Dillon 	if (mirror.tid_beg >= mirror.tid_end) {
155*48eadef9SMatthew Dillon 		if (streaming == 0 || VerboseOpt >= 2)
156*48eadef9SMatthew Dillon 			fprintf(stderr, "Mirror-read: No work to do\n");
157*48eadef9SMatthew Dillon 		didwork = 0;
1589dc76cb1SMatthew Dillon 		goto done;
1599dc76cb1SMatthew Dillon 	}
160*48eadef9SMatthew Dillon 	didwork = 1;
1619dc76cb1SMatthew Dillon 
1629dc76cb1SMatthew Dillon 	/*
163243ca327SMatthew Dillon 	 * Write out bulk records
164243ca327SMatthew Dillon 	 */
165a7fbbf91SMatthew Dillon 	mirror.ubuf = buf;
166a7fbbf91SMatthew Dillon 	mirror.size = SERIALBUF_SIZE;
167a7fbbf91SMatthew Dillon 
168a7fbbf91SMatthew Dillon 	do {
169a7fbbf91SMatthew Dillon 		mirror.count = 0;
170d4e5b69bSMatthew Dillon 		mirror.pfs_id = pfs.pfs_id;
171d4e5b69bSMatthew Dillon 		mirror.shared_uuid = pfs.ondisk->shared_uuid;
172a7fbbf91SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) {
173a7fbbf91SMatthew Dillon 			fprintf(stderr, "Mirror-read %s failed: %s\n",
174a7fbbf91SMatthew Dillon 				filesystem, strerror(errno));
175a7fbbf91SMatthew Dillon 			exit(1);
176a7fbbf91SMatthew Dillon 		}
1779c67b4d2SMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) {
1789c67b4d2SMatthew Dillon 			fprintf(stderr,
1799c67b4d2SMatthew Dillon 				"Mirror-read %s fatal error %d\n",
1809c67b4d2SMatthew Dillon 				filesystem, mirror.head.error);
1819c67b4d2SMatthew Dillon 			exit(1);
1829c67b4d2SMatthew Dillon 		}
183243ca327SMatthew Dillon 		if (mirror.count) {
184*48eadef9SMatthew Dillon 			if (BandwidthOpt) {
185*48eadef9SMatthew Dillon 				n = writebw(1, mirror.ubuf, mirror.count,
186*48eadef9SMatthew Dillon 					    &bwcount, &bwtv);
187*48eadef9SMatthew Dillon 			} else {
188243ca327SMatthew Dillon 				n = write(1, mirror.ubuf, mirror.count);
189*48eadef9SMatthew Dillon 			}
190243ca327SMatthew Dillon 			if (n != mirror.count) {
191243ca327SMatthew Dillon 				fprintf(stderr, "Mirror-read %s failed: "
192243ca327SMatthew Dillon 						"short write\n",
193243ca327SMatthew Dillon 				filesystem);
194243ca327SMatthew Dillon 				exit(1);
195243ca327SMatthew Dillon 			}
196a7fbbf91SMatthew Dillon 		}
197*48eadef9SMatthew Dillon 		total_bytes += mirror.count;
198*48eadef9SMatthew Dillon 		if (streaming && VerboseOpt) {
199*48eadef9SMatthew Dillon 			fprintf(stderr, "\r%016llx %11lld",
200*48eadef9SMatthew Dillon 				mirror.key_cur.obj_id,
201*48eadef9SMatthew Dillon 				total_bytes);
202*48eadef9SMatthew Dillon 			fflush(stderr);
203*48eadef9SMatthew Dillon 		}
204a7fbbf91SMatthew Dillon 		mirror.key_beg = mirror.key_cur;
205243ca327SMatthew Dillon 		if (TimeoutOpt &&
206243ca327SMatthew Dillon 		    (unsigned)(time(NULL) - base_t) > (unsigned)TimeoutOpt) {
207243ca327SMatthew Dillon 			fprintf(stderr,
208243ca327SMatthew Dillon 				"Mirror-read %s interrupted by timer at"
209243ca327SMatthew Dillon 				" %016llx\n",
210243ca327SMatthew Dillon 				filesystem,
211243ca327SMatthew Dillon 				mirror.key_cur.obj_id);
212243ca327SMatthew Dillon 			interrupted = 1;
213243ca327SMatthew Dillon 			break;
214243ca327SMatthew Dillon 		}
215a7fbbf91SMatthew Dillon 	} while (mirror.count != 0);
216a7fbbf91SMatthew Dillon 
217*48eadef9SMatthew Dillon done:
218243ca327SMatthew Dillon 	/*
219*48eadef9SMatthew Dillon 	 * Write out the termination sync record - only if not interrupted
220243ca327SMatthew Dillon 	 */
221*48eadef9SMatthew Dillon 	if (interrupted == 0) {
222*48eadef9SMatthew Dillon 		if (didwork) {
22317dd83bcSMatthew Dillon 			write_mrecord(1, HAMMER_MREC_TYPE_SYNC,
22417dd83bcSMatthew Dillon 				      &mrec_tmp, sizeof(mrec_tmp.sync));
225*48eadef9SMatthew Dillon 		} else {
226*48eadef9SMatthew Dillon 			write_mrecord(1, HAMMER_MREC_TYPE_IDLE,
227*48eadef9SMatthew Dillon 				      &mrec_tmp, sizeof(mrec_tmp.sync));
228*48eadef9SMatthew Dillon 		}
229*48eadef9SMatthew Dillon 	}
23034ebae70SMatthew Dillon 
231243ca327SMatthew Dillon 	/*
232243ca327SMatthew Dillon 	 * If the -2 option was given (automatic when doing mirror-copy),
233243ca327SMatthew Dillon 	 * a two-way pipe is assumed and we expect a response mrec from
234243ca327SMatthew Dillon 	 * the target.
235243ca327SMatthew Dillon 	 */
236243ca327SMatthew Dillon 	if (TwoWayPipeOpt) {
237*48eadef9SMatthew Dillon 		mrec = read_mrecord(0, &error, &pickup);
23817dd83bcSMatthew Dillon 		if (mrec == NULL ||
23917dd83bcSMatthew Dillon 		    mrec->head.type != HAMMER_MREC_TYPE_UPDATE ||
24017dd83bcSMatthew Dillon 		    mrec->head.rec_size != sizeof(mrec->update)) {
241243ca327SMatthew Dillon 			fprintf(stderr, "mirror_read: Did not get final "
242243ca327SMatthew Dillon 					"acknowledgement packet from target\n");
243243ca327SMatthew Dillon 			exit(1);
244243ca327SMatthew Dillon 		}
245243ca327SMatthew Dillon 		if (interrupted) {
246243ca327SMatthew Dillon 			if (CyclePath) {
247243ca327SMatthew Dillon 				hammer_set_cycle(&mirror.key_cur, mirror.tid_beg);
248243ca327SMatthew Dillon 				fprintf(stderr, "Cyclefile %s updated for continuation\n", CyclePath);
249243ca327SMatthew Dillon 			}
250243ca327SMatthew Dillon 		} else {
25117dd83bcSMatthew Dillon 			sync_tid = mrec->update.tid;
252243ca327SMatthew Dillon 			if (CyclePath) {
253243ca327SMatthew Dillon 				hammer_key_beg_init(&mirror.key_beg);
254243ca327SMatthew Dillon 				hammer_set_cycle(&mirror.key_beg, sync_tid);
255243ca327SMatthew Dillon 				fprintf(stderr, "Cyclefile %s updated to 0x%016llx\n",
256243ca327SMatthew Dillon 					CyclePath, sync_tid);
257243ca327SMatthew Dillon 			}
258243ca327SMatthew Dillon 		}
259243ca327SMatthew Dillon 	} else if (CyclePath) {
260243ca327SMatthew Dillon 		/* NOTE! mirror.tid_beg cannot be updated */
261243ca327SMatthew Dillon 		fprintf(stderr, "Warning: cycle file (-c option) cannot be "
262243ca327SMatthew Dillon 				"fully updated unless you use mirror-copy\n");
263243ca327SMatthew Dillon 		hammer_set_cycle(&mirror.key_beg, mirror.tid_beg);
264243ca327SMatthew Dillon 	}
265*48eadef9SMatthew Dillon 	if (streaming && interrupted == 0) {
266*48eadef9SMatthew Dillon 		time_t t1 = time(NULL);
267*48eadef9SMatthew Dillon 		time_t t2;
268*48eadef9SMatthew Dillon 
269*48eadef9SMatthew Dillon 		if (VerboseOpt) {
270*48eadef9SMatthew Dillon 			fprintf(stderr, " W");
271*48eadef9SMatthew Dillon 			fflush(stderr);
272*48eadef9SMatthew Dillon 		}
273*48eadef9SMatthew Dillon 		pfs.ondisk->sync_end_tid = mirror.tid_end;
274*48eadef9SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_WAI_PSEUDOFS, &pfs) < 0) {
275*48eadef9SMatthew Dillon 			fprintf(stderr, "Mirror-read %s: cannot stream: %s\n",
276*48eadef9SMatthew Dillon 				filesystem, strerror(errno));
277*48eadef9SMatthew Dillon 		} else {
278*48eadef9SMatthew Dillon 			t2 = time(NULL) - t1;
279*48eadef9SMatthew Dillon 			if (t2 >= 0 && t2 < DelayOpt) {
280*48eadef9SMatthew Dillon 				if (VerboseOpt) {
281*48eadef9SMatthew Dillon 					fprintf(stderr, "\bD");
282*48eadef9SMatthew Dillon 					fflush(stderr);
283*48eadef9SMatthew Dillon 				}
284*48eadef9SMatthew Dillon 				sleep(DelayOpt - t2);
285*48eadef9SMatthew Dillon 			}
286*48eadef9SMatthew Dillon 			if (VerboseOpt) {
287*48eadef9SMatthew Dillon 				fprintf(stderr, "\b ");
288*48eadef9SMatthew Dillon 				fflush(stderr);
289*48eadef9SMatthew Dillon 			}
290*48eadef9SMatthew Dillon 			relpfs(fd, &pfs);
291*48eadef9SMatthew Dillon 			goto again;
292*48eadef9SMatthew Dillon 		}
293*48eadef9SMatthew Dillon 	}
294*48eadef9SMatthew Dillon 	write_mrecord(1, HAMMER_MREC_TYPE_TERM,
295*48eadef9SMatthew Dillon 		      &mrec_tmp, sizeof(mrec_tmp.sync));
296*48eadef9SMatthew Dillon 	relpfs(fd, &pfs);
297a7fbbf91SMatthew Dillon 	fprintf(stderr, "Mirror-read %s succeeded\n", filesystem);
298a7fbbf91SMatthew Dillon }
299a7fbbf91SMatthew Dillon 
30017dd83bcSMatthew Dillon /*
30117dd83bcSMatthew Dillon  * Pipe the mirroring data stream on stdin to the HAMMER VFS, adding
30217dd83bcSMatthew Dillon  * some additional packet types to negotiate TID ranges and to verify
30317dd83bcSMatthew Dillon  * completion.  The HAMMER VFS does most of the work.
30417dd83bcSMatthew Dillon  *
30517dd83bcSMatthew Dillon  * It is important to note that the mirror.key_{beg,end} range must
30617dd83bcSMatthew Dillon  * match the ranged used by the original.  For now both sides use
30717dd83bcSMatthew Dillon  * range the entire key space.
30817dd83bcSMatthew Dillon  *
30917dd83bcSMatthew Dillon  * It is even more important that the records in the stream conform
31017dd83bcSMatthew Dillon  * to the TID range also supplied in the stream.  The HAMMER VFS will
31117dd83bcSMatthew Dillon  * use the REC, PASS, and SKIP record types to track the portions of
31217dd83bcSMatthew Dillon  * the B-Tree being scanned in order to be able to proactively delete
31317dd83bcSMatthew Dillon  * records on the target within those active areas that are not mentioned
31417dd83bcSMatthew Dillon  * by the source.
31517dd83bcSMatthew Dillon  *
31617dd83bcSMatthew Dillon  * The mirror.key_cur field is used by the VFS to do this tracking.  It
31717dd83bcSMatthew Dillon  * must be initialized to key_beg but then is persistently updated by
31817dd83bcSMatthew Dillon  * the HAMMER VFS on each successive ioctl() call.  If you blow up this
31917dd83bcSMatthew Dillon  * field you will blow up the mirror target, possibly to the point of
32017dd83bcSMatthew Dillon  * deleting everything.  As a safety measure the HAMMER VFS simply marks
32117dd83bcSMatthew Dillon  * the records that the source has destroyed as deleted on the target,
32217dd83bcSMatthew Dillon  * and normal pruning operations will deal with their final disposition
32317dd83bcSMatthew Dillon  * at some later time.
32417dd83bcSMatthew Dillon  */
325a7fbbf91SMatthew Dillon void
326a7fbbf91SMatthew Dillon hammer_cmd_mirror_write(char **av, int ac)
327a7fbbf91SMatthew Dillon {
328a7fbbf91SMatthew Dillon 	struct hammer_ioc_mirror_rw mirror;
329a7fbbf91SMatthew Dillon 	const char *filesystem;
330a7fbbf91SMatthew Dillon 	char *buf = malloc(SERIALBUF_SIZE);
331d4e5b69bSMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
33217dd83bcSMatthew Dillon 	struct hammer_ioc_mrecord_head pickup;
333243ca327SMatthew Dillon 	struct hammer_ioc_synctid synctid;
33417dd83bcSMatthew Dillon 	union hammer_ioc_mrecord_any mrec_tmp;
33517dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
336243ca327SMatthew Dillon 	int error;
337243ca327SMatthew Dillon 	int fd;
338*48eadef9SMatthew Dillon 	int n;
339a7fbbf91SMatthew Dillon 
340a7fbbf91SMatthew Dillon 	if (ac > 2)
341a7fbbf91SMatthew Dillon 		mirror_usage(1);
342a7fbbf91SMatthew Dillon 	filesystem = av[0];
343a7fbbf91SMatthew Dillon 
344*48eadef9SMatthew Dillon 	pickup.signature = 0;
345*48eadef9SMatthew Dillon 	pickup.type = 0;
346*48eadef9SMatthew Dillon 
347*48eadef9SMatthew Dillon again:
348a7fbbf91SMatthew Dillon 	bzero(&mirror, sizeof(mirror));
349a7fbbf91SMatthew Dillon 	hammer_key_beg_init(&mirror.key_beg);
350a7fbbf91SMatthew Dillon 	hammer_key_end_init(&mirror.key_end);
35117dd83bcSMatthew Dillon 	mirror.key_end = mirror.key_beg;
352a7fbbf91SMatthew Dillon 
353d4e5b69bSMatthew Dillon 	fd = getpfs(&pfs, filesystem);
354a7fbbf91SMatthew Dillon 
355243ca327SMatthew Dillon 	/*
356d4e5b69bSMatthew Dillon 	 * In two-way mode the target writes out a PFS packet first.
357d4e5b69bSMatthew Dillon 	 * The source uses our tid_end as its tid_beg by default,
358d4e5b69bSMatthew Dillon 	 * picking up where it left off.
359243ca327SMatthew Dillon 	 */
360d4e5b69bSMatthew Dillon 	mirror.tid_beg = 0;
361d4e5b69bSMatthew Dillon 	if (TwoWayPipeOpt) {
362d4e5b69bSMatthew Dillon 		generate_mrec_header(fd, 1, pfs.pfs_id,
363d4e5b69bSMatthew Dillon 				     &mirror.tid_beg, &mirror.tid_end);
364d4e5b69bSMatthew Dillon 	}
365d4e5b69bSMatthew Dillon 
366d4e5b69bSMatthew Dillon 	/*
36717dd83bcSMatthew Dillon 	 * Read and process the PFS header.  The source informs us of
36817dd83bcSMatthew Dillon 	 * the TID range the stream represents.
369d4e5b69bSMatthew Dillon 	 */
370*48eadef9SMatthew Dillon 	n = validate_mrec_header(fd, 0, 1, pfs.pfs_id, &pickup,
371d4e5b69bSMatthew Dillon 				 &mirror.tid_beg, &mirror.tid_end);
372*48eadef9SMatthew Dillon 	if (n < 0) {	/* got TERM record */
373*48eadef9SMatthew Dillon 		relpfs(fd, &pfs);
374*48eadef9SMatthew Dillon 		return;
375*48eadef9SMatthew Dillon 	}
37634ebae70SMatthew Dillon 
377a7fbbf91SMatthew Dillon 	mirror.ubuf = buf;
378a7fbbf91SMatthew Dillon 	mirror.size = SERIALBUF_SIZE;
379a7fbbf91SMatthew Dillon 
380243ca327SMatthew Dillon 	/*
38117dd83bcSMatthew Dillon 	 * Read and process bulk records (REC, PASS, and SKIP types).
38217dd83bcSMatthew Dillon 	 *
38317dd83bcSMatthew Dillon 	 * On your life, do NOT mess with mirror.key_cur or your mirror
38417dd83bcSMatthew Dillon 	 * target may become history.
385243ca327SMatthew Dillon 	 */
386a7fbbf91SMatthew Dillon 	for (;;) {
387a7fbbf91SMatthew Dillon 		mirror.count = 0;
388d4e5b69bSMatthew Dillon 		mirror.pfs_id = pfs.pfs_id;
389d4e5b69bSMatthew Dillon 		mirror.shared_uuid = pfs.ondisk->shared_uuid;
390a7fbbf91SMatthew Dillon 		mirror.size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup);
391a7fbbf91SMatthew Dillon 		if (mirror.size <= 0)
392a7fbbf91SMatthew Dillon 			break;
393a7fbbf91SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_MIRROR_WRITE, &mirror) < 0) {
394a7fbbf91SMatthew Dillon 			fprintf(stderr, "Mirror-write %s failed: %s\n",
395a7fbbf91SMatthew Dillon 				filesystem, strerror(errno));
396a7fbbf91SMatthew Dillon 			exit(1);
397a7fbbf91SMatthew Dillon 		}
39817dd83bcSMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) {
39917dd83bcSMatthew Dillon 			fprintf(stderr,
40017dd83bcSMatthew Dillon 				"Mirror-write %s fatal error %d\n",
40117dd83bcSMatthew Dillon 				filesystem, mirror.head.error);
40217dd83bcSMatthew Dillon 			exit(1);
40317dd83bcSMatthew Dillon 		}
404243ca327SMatthew Dillon #if 0
405a7fbbf91SMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_INTR) {
406a7fbbf91SMatthew Dillon 			fprintf(stderr,
407a7fbbf91SMatthew Dillon 				"Mirror-write %s interrupted by timer at"
408243ca327SMatthew Dillon 				" %016llx\n",
409a7fbbf91SMatthew Dillon 				filesystem,
410243ca327SMatthew Dillon 				mirror.key_cur.obj_id);
411a7fbbf91SMatthew Dillon 			exit(0);
412a7fbbf91SMatthew Dillon 		}
413243ca327SMatthew Dillon #endif
414a7fbbf91SMatthew Dillon 	}
415243ca327SMatthew Dillon 
416243ca327SMatthew Dillon 	/*
417243ca327SMatthew Dillon 	 * Read and process the termination sync record.
418243ca327SMatthew Dillon 	 */
419243ca327SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
4209dc76cb1SMatthew Dillon 
4219dc76cb1SMatthew Dillon 	if (mrec && mrec->head.type == HAMMER_MREC_TYPE_TERM) {
422*48eadef9SMatthew Dillon 		fprintf(stderr, "Mirror-write: received termination request\n");
423*48eadef9SMatthew Dillon 		free(mrec);
4249dc76cb1SMatthew Dillon 		return;
4259dc76cb1SMatthew Dillon 	}
4269dc76cb1SMatthew Dillon 
42717dd83bcSMatthew Dillon 	if (mrec == NULL ||
428*48eadef9SMatthew Dillon 	    (mrec->head.type != HAMMER_MREC_TYPE_SYNC &&
429*48eadef9SMatthew Dillon 	     mrec->head.type != HAMMER_MREC_TYPE_IDLE) ||
43017dd83bcSMatthew Dillon 	    mrec->head.rec_size != sizeof(mrec->sync)) {
431243ca327SMatthew Dillon 		fprintf(stderr, "Mirror-write %s: Did not get termination "
43217dd83bcSMatthew Dillon 				"sync record, or rec_size is wrong rt=%d\n",
43317dd83bcSMatthew Dillon 				filesystem, mrec->head.type);
434da44aa75SMatthew Dillon 		exit(1);
435243ca327SMatthew Dillon 	}
436243ca327SMatthew Dillon 
437243ca327SMatthew Dillon 	/*
438243ca327SMatthew Dillon 	 * Update the PFS info on the target so the user has visibility
439*48eadef9SMatthew Dillon 	 * into the new snapshot, and sync the target filesystem.
440243ca327SMatthew Dillon 	 */
441*48eadef9SMatthew Dillon 	if (mrec->head.type == HAMMER_MREC_TYPE_SYNC) {
442d4e5b69bSMatthew Dillon 		update_pfs_snapshot(fd, mirror.tid_end, pfs.pfs_id);
443243ca327SMatthew Dillon 
444243ca327SMatthew Dillon 		bzero(&synctid, sizeof(synctid));
445243ca327SMatthew Dillon 		synctid.op = HAMMER_SYNCTID_SYNC2;
446243ca327SMatthew Dillon 		ioctl(fd, HAMMERIOC_SYNCTID, &synctid);
447243ca327SMatthew Dillon 
448*48eadef9SMatthew Dillon 		if (VerboseOpt >= 2) {
449*48eadef9SMatthew Dillon 			fprintf(stderr, "Mirror-write %s: succeeded\n",
450*48eadef9SMatthew Dillon 				filesystem);
451*48eadef9SMatthew Dillon 		}
452*48eadef9SMatthew Dillon 	}
453*48eadef9SMatthew Dillon 
454*48eadef9SMatthew Dillon 	free(mrec);
455*48eadef9SMatthew Dillon 	mrec = NULL;
456243ca327SMatthew Dillon 
457243ca327SMatthew Dillon 	/*
458243ca327SMatthew Dillon 	 * Report back to the originator.
459243ca327SMatthew Dillon 	 */
460243ca327SMatthew Dillon 	if (TwoWayPipeOpt) {
46117dd83bcSMatthew Dillon 		mrec_tmp.update.tid = mirror.tid_end;
462243ca327SMatthew Dillon 		write_mrecord(1, HAMMER_MREC_TYPE_UPDATE,
46317dd83bcSMatthew Dillon 			      &mrec_tmp, sizeof(mrec_tmp.update));
464243ca327SMatthew Dillon 	} else {
465243ca327SMatthew Dillon 		printf("Source can update synctid to 0x%016llx\n",
466243ca327SMatthew Dillon 		       mirror.tid_end);
467243ca327SMatthew Dillon 	}
468*48eadef9SMatthew Dillon 	relpfs(fd, &pfs);
469*48eadef9SMatthew Dillon 	goto again;
470243ca327SMatthew Dillon }
471243ca327SMatthew Dillon 
472243ca327SMatthew Dillon void
473243ca327SMatthew Dillon hammer_cmd_mirror_dump(void)
474243ca327SMatthew Dillon {
475243ca327SMatthew Dillon 	char *buf = malloc(SERIALBUF_SIZE);
47617dd83bcSMatthew Dillon 	struct hammer_ioc_mrecord_head pickup;
47717dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
478243ca327SMatthew Dillon 	int error;
479243ca327SMatthew Dillon 	int size;
48017dd83bcSMatthew Dillon 	int offset;
48117dd83bcSMatthew Dillon 	int bytes;
482243ca327SMatthew Dillon 
483243ca327SMatthew Dillon 	/*
484243ca327SMatthew Dillon 	 * Read and process the PFS header
485243ca327SMatthew Dillon 	 */
486243ca327SMatthew Dillon 	pickup.signature = 0;
487243ca327SMatthew Dillon 	pickup.type = 0;
488243ca327SMatthew Dillon 
489243ca327SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
490243ca327SMatthew Dillon 
491243ca327SMatthew Dillon 	/*
492243ca327SMatthew Dillon 	 * Read and process bulk records
493243ca327SMatthew Dillon 	 */
494243ca327SMatthew Dillon 	for (;;) {
495243ca327SMatthew Dillon 		size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup);
496243ca327SMatthew Dillon 		if (size <= 0)
497243ca327SMatthew Dillon 			break;
49817dd83bcSMatthew Dillon 		offset = 0;
49917dd83bcSMatthew Dillon 		while (offset < size) {
50017dd83bcSMatthew Dillon 			mrec = (void *)((char *)buf + offset);
50117dd83bcSMatthew Dillon 			bytes = HAMMER_HEAD_DOALIGN(mrec->head.rec_size);
50217dd83bcSMatthew Dillon 			if (offset + bytes > size) {
50317dd83bcSMatthew Dillon 				fprintf(stderr, "Misaligned record\n");
50417dd83bcSMatthew Dillon 				exit(1);
50517dd83bcSMatthew Dillon 			}
50617dd83bcSMatthew Dillon 
50717dd83bcSMatthew Dillon 			switch(mrec->head.type) {
50817dd83bcSMatthew Dillon 			case HAMMER_MREC_TYPE_REC:
509243ca327SMatthew Dillon 				printf("Record obj=%016llx key=%016llx "
510243ca327SMatthew Dillon 				       "rt=%02x ot=%02x\n",
51117dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_id,
51217dd83bcSMatthew Dillon 					mrec->rec.leaf.base.key,
51317dd83bcSMatthew Dillon 					mrec->rec.leaf.base.rec_type,
51417dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_type);
515243ca327SMatthew Dillon 				printf("       tids %016llx:%016llx data=%d\n",
51617dd83bcSMatthew Dillon 					mrec->rec.leaf.base.create_tid,
51717dd83bcSMatthew Dillon 					mrec->rec.leaf.base.delete_tid,
51817dd83bcSMatthew Dillon 					mrec->rec.leaf.data_len);
51917dd83bcSMatthew Dillon 				break;
52017dd83bcSMatthew Dillon 			case HAMMER_MREC_TYPE_PASS:
52117dd83bcSMatthew Dillon 				printf("Pass   obj=%016llx key=%016llx "
52217dd83bcSMatthew Dillon 				       "rt=%02x ot=%02x\n",
52317dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_id,
52417dd83bcSMatthew Dillon 					mrec->rec.leaf.base.key,
52517dd83bcSMatthew Dillon 					mrec->rec.leaf.base.rec_type,
52617dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_type);
52717dd83bcSMatthew Dillon 				printf("       tids %016llx:%016llx data=%d\n",
52817dd83bcSMatthew Dillon 					mrec->rec.leaf.base.create_tid,
52917dd83bcSMatthew Dillon 					mrec->rec.leaf.base.delete_tid,
53017dd83bcSMatthew Dillon 					mrec->rec.leaf.data_len);
53117dd83bcSMatthew Dillon 				break;
53217dd83bcSMatthew Dillon 			case HAMMER_MREC_TYPE_SKIP:
53317dd83bcSMatthew Dillon 				printf("Skip   obj=%016llx key=%016llx rt=%02x to\n"
53417dd83bcSMatthew Dillon 				       "       obj=%016llx key=%016llx rt=%02x\n",
53517dd83bcSMatthew Dillon 				       mrec->skip.skip_beg.obj_id,
53617dd83bcSMatthew Dillon 				       mrec->skip.skip_beg.key,
53717dd83bcSMatthew Dillon 				       mrec->skip.skip_beg.rec_type,
53817dd83bcSMatthew Dillon 				       mrec->skip.skip_end.obj_id,
53917dd83bcSMatthew Dillon 				       mrec->skip.skip_end.key,
54017dd83bcSMatthew Dillon 				       mrec->skip.skip_end.rec_type);
54117dd83bcSMatthew Dillon 			default:
54217dd83bcSMatthew Dillon 				break;
54317dd83bcSMatthew Dillon 			}
54417dd83bcSMatthew Dillon 			offset += bytes;
545243ca327SMatthew Dillon 		}
546243ca327SMatthew Dillon 	}
547243ca327SMatthew Dillon 
548243ca327SMatthew Dillon 	/*
549243ca327SMatthew Dillon 	 * Read and process the termination sync record.
550243ca327SMatthew Dillon 	 */
551243ca327SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
552*48eadef9SMatthew Dillon 	if (mrec == NULL ||
553*48eadef9SMatthew Dillon 	    (mrec->head.type != HAMMER_MREC_TYPE_SYNC &&
554*48eadef9SMatthew Dillon 	     mrec->head.type != HAMMER_MREC_TYPE_IDLE)
555*48eadef9SMatthew Dillon 	 ) {
556243ca327SMatthew Dillon 		fprintf(stderr, "Mirror-dump: Did not get termination "
557243ca327SMatthew Dillon 				"sync record\n");
558243ca327SMatthew Dillon 	}
559a7fbbf91SMatthew Dillon }
560a7fbbf91SMatthew Dillon 
561a7fbbf91SMatthew Dillon void
562*48eadef9SMatthew Dillon hammer_cmd_mirror_copy(char **av, int ac, int streaming)
563a7fbbf91SMatthew Dillon {
56434ebae70SMatthew Dillon 	pid_t pid1;
56534ebae70SMatthew Dillon 	pid_t pid2;
56634ebae70SMatthew Dillon 	int fds[2];
567243ca327SMatthew Dillon 	const char *xav[16];
568243ca327SMatthew Dillon 	char tbuf[16];
56934ebae70SMatthew Dillon 	char *ptr;
570243ca327SMatthew Dillon 	int xac;
57134ebae70SMatthew Dillon 
57234ebae70SMatthew Dillon 	if (ac != 2)
57334ebae70SMatthew Dillon 		mirror_usage(1);
57434ebae70SMatthew Dillon 
57534ebae70SMatthew Dillon 	if (pipe(fds) < 0) {
57634ebae70SMatthew Dillon 		perror("pipe");
57734ebae70SMatthew Dillon 		exit(1);
57834ebae70SMatthew Dillon 	}
57934ebae70SMatthew Dillon 
580243ca327SMatthew Dillon 	TwoWayPipeOpt = 1;
581243ca327SMatthew Dillon 
58234ebae70SMatthew Dillon 	/*
58334ebae70SMatthew Dillon 	 * Source
58434ebae70SMatthew Dillon 	 */
58534ebae70SMatthew Dillon 	if ((pid1 = fork()) == 0) {
58634ebae70SMatthew Dillon 		dup2(fds[0], 0);
58734ebae70SMatthew Dillon 		dup2(fds[0], 1);
58834ebae70SMatthew Dillon 		close(fds[0]);
58934ebae70SMatthew Dillon 		close(fds[1]);
59034ebae70SMatthew Dillon 		if ((ptr = strchr(av[0], ':')) != NULL) {
59134ebae70SMatthew Dillon 			*ptr++ = 0;
592243ca327SMatthew Dillon 			xac = 0;
593243ca327SMatthew Dillon 			xav[xac++] = "ssh";
594243ca327SMatthew Dillon 			xav[xac++] = av[0];
595243ca327SMatthew Dillon 			xav[xac++] = "hammer";
596*48eadef9SMatthew Dillon 
597*48eadef9SMatthew Dillon 			switch(VerboseOpt) {
598*48eadef9SMatthew Dillon 			case 0:
599*48eadef9SMatthew Dillon 				break;
600*48eadef9SMatthew Dillon 			case 1:
601243ca327SMatthew Dillon 				xav[xac++] = "-v";
602*48eadef9SMatthew Dillon 				break;
603*48eadef9SMatthew Dillon 			case 2:
604*48eadef9SMatthew Dillon 				xav[xac++] = "-vv";
605*48eadef9SMatthew Dillon 				break;
606*48eadef9SMatthew Dillon 			default:
607*48eadef9SMatthew Dillon 				xav[xac++] = "-vvv";
608*48eadef9SMatthew Dillon 				break;
609*48eadef9SMatthew Dillon 			}
610243ca327SMatthew Dillon 			xav[xac++] = "-2";
611243ca327SMatthew Dillon 			if (TimeoutOpt) {
612243ca327SMatthew Dillon 				snprintf(tbuf, sizeof(tbuf), "%d", TimeoutOpt);
613243ca327SMatthew Dillon 				xav[xac++] = "-t";
614243ca327SMatthew Dillon 				xav[xac++] = tbuf;
615243ca327SMatthew Dillon 			}
616*48eadef9SMatthew Dillon 			if (streaming)
617*48eadef9SMatthew Dillon 				xav[xac++] = "mirror-read-streaming";
618*48eadef9SMatthew Dillon 			else
619243ca327SMatthew Dillon 				xav[xac++] = "mirror-read";
620243ca327SMatthew Dillon 			xav[xac++] = ptr;
621243ca327SMatthew Dillon 			xav[xac++] = NULL;
622243ca327SMatthew Dillon 			execv("/usr/bin/ssh", (void *)xav);
62334ebae70SMatthew Dillon 		} else {
624*48eadef9SMatthew Dillon 			hammer_cmd_mirror_read(av, 1, streaming);
625243ca327SMatthew Dillon 			fflush(stdout);
626243ca327SMatthew Dillon 			fflush(stderr);
62734ebae70SMatthew Dillon 		}
62853d93cc7SMatthew Dillon 		_exit(1);
62934ebae70SMatthew Dillon 	}
63034ebae70SMatthew Dillon 
63134ebae70SMatthew Dillon 	/*
63234ebae70SMatthew Dillon 	 * Target
63334ebae70SMatthew Dillon 	 */
63434ebae70SMatthew Dillon 	if ((pid2 = fork()) == 0) {
63534ebae70SMatthew Dillon 		dup2(fds[1], 0);
63634ebae70SMatthew Dillon 		dup2(fds[1], 1);
63734ebae70SMatthew Dillon 		close(fds[0]);
63834ebae70SMatthew Dillon 		close(fds[1]);
63934ebae70SMatthew Dillon 		if ((ptr = strchr(av[1], ':')) != NULL) {
64034ebae70SMatthew Dillon 			*ptr++ = 0;
641243ca327SMatthew Dillon 			xac = 0;
642243ca327SMatthew Dillon 			xav[xac++] = "ssh";
643243ca327SMatthew Dillon 			xav[xac++] = av[1];
644243ca327SMatthew Dillon 			xav[xac++] = "hammer";
645*48eadef9SMatthew Dillon 
646*48eadef9SMatthew Dillon 			switch(VerboseOpt) {
647*48eadef9SMatthew Dillon 			case 0:
648*48eadef9SMatthew Dillon 				break;
649*48eadef9SMatthew Dillon 			case 1:
650243ca327SMatthew Dillon 				xav[xac++] = "-v";
651*48eadef9SMatthew Dillon 				break;
652*48eadef9SMatthew Dillon 			case 2:
653*48eadef9SMatthew Dillon 				xav[xac++] = "-vv";
654*48eadef9SMatthew Dillon 				break;
655*48eadef9SMatthew Dillon 			default:
656*48eadef9SMatthew Dillon 				xav[xac++] = "-vvv";
657*48eadef9SMatthew Dillon 				break;
658*48eadef9SMatthew Dillon 			}
659*48eadef9SMatthew Dillon 
660243ca327SMatthew Dillon 			xav[xac++] = "-2";
661243ca327SMatthew Dillon 			xav[xac++] = "mirror-write";
662243ca327SMatthew Dillon 			xav[xac++] = ptr;
663243ca327SMatthew Dillon 			xav[xac++] = NULL;
664243ca327SMatthew Dillon 			execv("/usr/bin/ssh", (void *)xav);
66534ebae70SMatthew Dillon 		} else {
66634ebae70SMatthew Dillon 			hammer_cmd_mirror_write(av + 1, 1);
667243ca327SMatthew Dillon 			fflush(stdout);
668243ca327SMatthew Dillon 			fflush(stderr);
66934ebae70SMatthew Dillon 		}
67053d93cc7SMatthew Dillon 		_exit(1);
67134ebae70SMatthew Dillon 	}
67234ebae70SMatthew Dillon 	close(fds[0]);
67334ebae70SMatthew Dillon 	close(fds[1]);
67434ebae70SMatthew Dillon 
67534ebae70SMatthew Dillon 	while (waitpid(pid1, NULL, 0) <= 0)
67634ebae70SMatthew Dillon 		;
67734ebae70SMatthew Dillon 	while (waitpid(pid2, NULL, 0) <= 0)
67834ebae70SMatthew Dillon 		;
679a7fbbf91SMatthew Dillon }
680a7fbbf91SMatthew Dillon 
681243ca327SMatthew Dillon /*
682243ca327SMatthew Dillon  * Read and return multiple mrecords
683243ca327SMatthew Dillon  */
684a7fbbf91SMatthew Dillon static int
68517dd83bcSMatthew Dillon read_mrecords(int fd, char *buf, u_int size, hammer_ioc_mrecord_head_t pickup)
686a7fbbf91SMatthew Dillon {
68717dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
688a7fbbf91SMatthew Dillon 	u_int count;
689a7fbbf91SMatthew Dillon 	size_t n;
690a7fbbf91SMatthew Dillon 	size_t i;
69117dd83bcSMatthew Dillon 	size_t bytes;
692a7fbbf91SMatthew Dillon 
693a7fbbf91SMatthew Dillon 	count = 0;
694a7fbbf91SMatthew Dillon 	while (size - count >= HAMMER_MREC_HEADSIZE) {
695a7fbbf91SMatthew Dillon 		/*
696a7fbbf91SMatthew Dillon 		 * Cached the record header in case we run out of buffer
697a7fbbf91SMatthew Dillon 		 * space.
698a7fbbf91SMatthew Dillon 		 */
69917dd83bcSMatthew Dillon 		fflush(stdout);
700a7fbbf91SMatthew Dillon 		if (pickup->signature == 0) {
701a7fbbf91SMatthew Dillon 			for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) {
702a7fbbf91SMatthew Dillon 				i = read(fd, (char *)pickup + n,
703a7fbbf91SMatthew Dillon 					 HAMMER_MREC_HEADSIZE - n);
704a7fbbf91SMatthew Dillon 				if (i <= 0)
705a7fbbf91SMatthew Dillon 					break;
706a7fbbf91SMatthew Dillon 			}
707a7fbbf91SMatthew Dillon 			if (n == 0)
708a7fbbf91SMatthew Dillon 				break;
709a7fbbf91SMatthew Dillon 			if (n != HAMMER_MREC_HEADSIZE) {
710a7fbbf91SMatthew Dillon 				fprintf(stderr, "read_mrecords: short read on pipe\n");
711a7fbbf91SMatthew Dillon 				exit(1);
712a7fbbf91SMatthew Dillon 			}
713a7fbbf91SMatthew Dillon 
714a7fbbf91SMatthew Dillon 			if (pickup->signature != HAMMER_IOC_MIRROR_SIGNATURE) {
715a7fbbf91SMatthew Dillon 				fprintf(stderr, "read_mrecords: malformed record on pipe, bad signature\n");
716a7fbbf91SMatthew Dillon 				exit(1);
717a7fbbf91SMatthew Dillon 			}
718a7fbbf91SMatthew Dillon 		}
719a7fbbf91SMatthew Dillon 		if (pickup->rec_size < HAMMER_MREC_HEADSIZE ||
72017dd83bcSMatthew Dillon 		    pickup->rec_size > sizeof(*mrec) + HAMMER_XBUFSIZE) {
721a7fbbf91SMatthew Dillon 			fprintf(stderr, "read_mrecords: malformed record on pipe, illegal rec_size\n");
722a7fbbf91SMatthew Dillon 			exit(1);
723a7fbbf91SMatthew Dillon 		}
724a7fbbf91SMatthew Dillon 
725a7fbbf91SMatthew Dillon 		/*
726a7fbbf91SMatthew Dillon 		 * Stop if we have insufficient space for the record and data.
727a7fbbf91SMatthew Dillon 		 */
72817dd83bcSMatthew Dillon 		bytes = HAMMER_HEAD_DOALIGN(pickup->rec_size);
72917dd83bcSMatthew Dillon 		if (size - count < bytes)
730a7fbbf91SMatthew Dillon 			break;
731a7fbbf91SMatthew Dillon 
732a7fbbf91SMatthew Dillon 		/*
73317dd83bcSMatthew Dillon 		 * Stop if the record type is not a REC or a SKIP (the only
73417dd83bcSMatthew Dillon 		 * two types the ioctl supports.  Other types are used only
73517dd83bcSMatthew Dillon 		 * by the userland protocol).
736243ca327SMatthew Dillon 		 */
73717dd83bcSMatthew Dillon 		if (pickup->type != HAMMER_MREC_TYPE_REC &&
73817dd83bcSMatthew Dillon 		    pickup->type != HAMMER_MREC_TYPE_SKIP &&
73917dd83bcSMatthew Dillon 		    pickup->type != HAMMER_MREC_TYPE_PASS) {
740243ca327SMatthew Dillon 			break;
74117dd83bcSMatthew Dillon 		}
742243ca327SMatthew Dillon 
743243ca327SMatthew Dillon 		/*
744a7fbbf91SMatthew Dillon 		 * Read the remainder and clear the pickup signature.
745a7fbbf91SMatthew Dillon 		 */
74617dd83bcSMatthew Dillon 		for (n = HAMMER_MREC_HEADSIZE; n < bytes; n += i) {
74717dd83bcSMatthew Dillon 			i = read(fd, buf + count + n, bytes - n);
748a7fbbf91SMatthew Dillon 			if (i <= 0)
749a7fbbf91SMatthew Dillon 				break;
750a7fbbf91SMatthew Dillon 		}
75117dd83bcSMatthew Dillon 		if (n != bytes) {
752a7fbbf91SMatthew Dillon 			fprintf(stderr, "read_mrecords: short read on pipe\n");
753a7fbbf91SMatthew Dillon 			exit(1);
754a7fbbf91SMatthew Dillon 		}
75517dd83bcSMatthew Dillon 
75617dd83bcSMatthew Dillon 		bcopy(pickup, buf + count, HAMMER_MREC_HEADSIZE);
75717dd83bcSMatthew Dillon 		pickup->signature = 0;
75817dd83bcSMatthew Dillon 		pickup->type = 0;
75917dd83bcSMatthew Dillon 		mrec = (void *)(buf + count);
76017dd83bcSMatthew Dillon 
76117dd83bcSMatthew Dillon 		/*
76217dd83bcSMatthew Dillon 		 * Validate the completed record
76317dd83bcSMatthew Dillon 		 */
76417dd83bcSMatthew Dillon 		if (mrec->head.rec_crc !=
76517dd83bcSMatthew Dillon 		    crc32((char *)mrec + HAMMER_MREC_CRCOFF,
76617dd83bcSMatthew Dillon 			  mrec->head.rec_size - HAMMER_MREC_CRCOFF)) {
76717dd83bcSMatthew Dillon 			fprintf(stderr, "read_mrecords: malformed record "
76817dd83bcSMatthew Dillon 					"on pipe, bad crc\n");
76917dd83bcSMatthew Dillon 			exit(1);
770a7fbbf91SMatthew Dillon 		}
771a7fbbf91SMatthew Dillon 
77217dd83bcSMatthew Dillon 		/*
77317dd83bcSMatthew Dillon 		 * If its a B-Tree record validate the data crc
77417dd83bcSMatthew Dillon 		 */
77517dd83bcSMatthew Dillon 		if (mrec->head.type == HAMMER_MREC_TYPE_REC) {
77617dd83bcSMatthew Dillon 			if (mrec->head.rec_size <
77717dd83bcSMatthew Dillon 			    sizeof(mrec->rec) + mrec->rec.leaf.data_len) {
77817dd83bcSMatthew Dillon 				fprintf(stderr,
77917dd83bcSMatthew Dillon 					"read_mrecords: malformed record on "
78017dd83bcSMatthew Dillon 					"pipe, illegal element data_len\n");
78117dd83bcSMatthew Dillon 				exit(1);
78217dd83bcSMatthew Dillon 			}
78317dd83bcSMatthew Dillon 			if (mrec->rec.leaf.data_len &&
78417dd83bcSMatthew Dillon 			    mrec->rec.leaf.data_offset &&
78517dd83bcSMatthew Dillon 			    hammer_crc_test_leaf(&mrec->rec + 1, &mrec->rec.leaf) == 0) {
78617dd83bcSMatthew Dillon 				fprintf(stderr,
78717dd83bcSMatthew Dillon 					"read_mrecords: data_crc did not "
78817dd83bcSMatthew Dillon 					"match data! obj=%016llx key=%016llx\n",
78917dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_id,
79017dd83bcSMatthew Dillon 					mrec->rec.leaf.base.key);
79117dd83bcSMatthew Dillon 				fprintf(stderr,
79217dd83bcSMatthew Dillon 					"continuing, but there are problems\n");
79317dd83bcSMatthew Dillon 			}
79417dd83bcSMatthew Dillon 		}
79517dd83bcSMatthew Dillon 		count += bytes;
796a7fbbf91SMatthew Dillon 	}
797a7fbbf91SMatthew Dillon 	return(count);
798a7fbbf91SMatthew Dillon }
799a7fbbf91SMatthew Dillon 
80034ebae70SMatthew Dillon /*
80117dd83bcSMatthew Dillon  * Read and return a single mrecord.
802243ca327SMatthew Dillon  */
803243ca327SMatthew Dillon static
80417dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t
80517dd83bcSMatthew Dillon read_mrecord(int fdin, int *errorp, hammer_ioc_mrecord_head_t pickup)
806243ca327SMatthew Dillon {
80717dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
80817dd83bcSMatthew Dillon 	struct hammer_ioc_mrecord_head mrechd;
809243ca327SMatthew Dillon 	size_t bytes;
810243ca327SMatthew Dillon 	size_t n;
811243ca327SMatthew Dillon 	size_t i;
812243ca327SMatthew Dillon 
813243ca327SMatthew Dillon 	if (pickup && pickup->type != 0) {
814243ca327SMatthew Dillon 		mrechd = *pickup;
815243ca327SMatthew Dillon 		pickup->signature = 0;
816243ca327SMatthew Dillon 		pickup->type = 0;
817243ca327SMatthew Dillon 		n = HAMMER_MREC_HEADSIZE;
818243ca327SMatthew Dillon 	} else {
819243ca327SMatthew Dillon 		/*
820243ca327SMatthew Dillon 		 * Read in the PFSD header from the sender.
821243ca327SMatthew Dillon 		 */
822243ca327SMatthew Dillon 		for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) {
823243ca327SMatthew Dillon 			i = read(fdin, (char *)&mrechd + n, HAMMER_MREC_HEADSIZE - n);
824243ca327SMatthew Dillon 			if (i <= 0)
825243ca327SMatthew Dillon 				break;
826243ca327SMatthew Dillon 		}
827243ca327SMatthew Dillon 		if (n == 0) {
828243ca327SMatthew Dillon 			*errorp = 0;	/* EOF */
829243ca327SMatthew Dillon 			return(NULL);
830243ca327SMatthew Dillon 		}
831243ca327SMatthew Dillon 		if (n != HAMMER_MREC_HEADSIZE) {
832243ca327SMatthew Dillon 			fprintf(stderr, "short read of mrecord header\n");
833243ca327SMatthew Dillon 			*errorp = EPIPE;
834243ca327SMatthew Dillon 			return(NULL);
835243ca327SMatthew Dillon 		}
836243ca327SMatthew Dillon 	}
837243ca327SMatthew Dillon 	if (mrechd.signature != HAMMER_IOC_MIRROR_SIGNATURE) {
838243ca327SMatthew Dillon 		fprintf(stderr, "read_mrecord: bad signature\n");
839243ca327SMatthew Dillon 		*errorp = EINVAL;
840243ca327SMatthew Dillon 		return(NULL);
841243ca327SMatthew Dillon 	}
84217dd83bcSMatthew Dillon 	bytes = HAMMER_HEAD_DOALIGN(mrechd.rec_size);
84317dd83bcSMatthew Dillon 	assert(bytes >= sizeof(mrechd));
844243ca327SMatthew Dillon 	mrec = malloc(bytes);
84517dd83bcSMatthew Dillon 	mrec->head = mrechd;
84617dd83bcSMatthew Dillon 
847243ca327SMatthew Dillon 	while (n < bytes) {
848243ca327SMatthew Dillon 		i = read(fdin, (char *)mrec + n, bytes - n);
849243ca327SMatthew Dillon 		if (i <= 0)
850243ca327SMatthew Dillon 			break;
851243ca327SMatthew Dillon 		n += i;
852243ca327SMatthew Dillon 	}
853243ca327SMatthew Dillon 	if (n != bytes) {
854243ca327SMatthew Dillon 		fprintf(stderr, "read_mrecord: short read on payload\n");
855243ca327SMatthew Dillon 		*errorp = EPIPE;
856243ca327SMatthew Dillon 		return(NULL);
857243ca327SMatthew Dillon 	}
85817dd83bcSMatthew Dillon 	if (mrec->head.rec_crc !=
85917dd83bcSMatthew Dillon 	    crc32((char *)mrec + HAMMER_MREC_CRCOFF,
86017dd83bcSMatthew Dillon 		  mrec->head.rec_size - HAMMER_MREC_CRCOFF)) {
861243ca327SMatthew Dillon 		fprintf(stderr, "read_mrecord: bad CRC\n");
862243ca327SMatthew Dillon 		*errorp = EINVAL;
863243ca327SMatthew Dillon 		return(NULL);
864243ca327SMatthew Dillon 	}
865243ca327SMatthew Dillon 	*errorp = 0;
866243ca327SMatthew Dillon 	return(mrec);
867243ca327SMatthew Dillon }
868243ca327SMatthew Dillon 
869243ca327SMatthew Dillon static
870243ca327SMatthew Dillon void
87117dd83bcSMatthew Dillon write_mrecord(int fdout, u_int32_t type, hammer_ioc_mrecord_any_t mrec,
87217dd83bcSMatthew Dillon 	      int bytes)
873243ca327SMatthew Dillon {
87417dd83bcSMatthew Dillon 	char zbuf[HAMMER_HEAD_ALIGN];
87517dd83bcSMatthew Dillon 	int pad;
876243ca327SMatthew Dillon 
87717dd83bcSMatthew Dillon 	pad = HAMMER_HEAD_DOALIGN(bytes) - bytes;
87817dd83bcSMatthew Dillon 
87917dd83bcSMatthew Dillon 	assert(bytes >= (int)sizeof(mrec->head));
88017dd83bcSMatthew Dillon 	bzero(&mrec->head, sizeof(mrec->head));
88117dd83bcSMatthew Dillon 	mrec->head.signature = HAMMER_IOC_MIRROR_SIGNATURE;
88217dd83bcSMatthew Dillon 	mrec->head.type = type;
88317dd83bcSMatthew Dillon 	mrec->head.rec_size = bytes;
88417dd83bcSMatthew Dillon 	mrec->head.rec_crc = crc32((char *)mrec + HAMMER_MREC_CRCOFF,
88517dd83bcSMatthew Dillon 				   bytes - HAMMER_MREC_CRCOFF);
88617dd83bcSMatthew Dillon 	if (write(fdout, mrec, bytes) != bytes) {
887243ca327SMatthew Dillon 		fprintf(stderr, "write_mrecord: error %d (%s)\n",
888243ca327SMatthew Dillon 			errno, strerror(errno));
889243ca327SMatthew Dillon 		exit(1);
890243ca327SMatthew Dillon 	}
89117dd83bcSMatthew Dillon 	if (pad) {
89217dd83bcSMatthew Dillon 		bzero(zbuf, pad);
89317dd83bcSMatthew Dillon 		if (write(fdout, zbuf, pad) != pad) {
89417dd83bcSMatthew Dillon 			fprintf(stderr, "write_mrecord: error %d (%s)\n",
89517dd83bcSMatthew Dillon 				errno, strerror(errno));
89617dd83bcSMatthew Dillon 			exit(1);
89717dd83bcSMatthew Dillon 		}
89817dd83bcSMatthew Dillon 	}
899243ca327SMatthew Dillon }
900243ca327SMatthew Dillon 
901243ca327SMatthew Dillon /*
90234ebae70SMatthew Dillon  * Generate a mirroring header with the pfs information of the
90334ebae70SMatthew Dillon  * originating filesytem.
90434ebae70SMatthew Dillon  */
90534ebae70SMatthew Dillon static void
906d4e5b69bSMatthew Dillon generate_mrec_header(int fd, int fdout, int pfs_id,
90734ebae70SMatthew Dillon 		     hammer_tid_t *tid_begp, hammer_tid_t *tid_endp)
90834ebae70SMatthew Dillon {
90934ebae70SMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
91017dd83bcSMatthew Dillon 	union hammer_ioc_mrecord_any mrec_tmp;
91134ebae70SMatthew Dillon 
91234ebae70SMatthew Dillon 	bzero(&pfs, sizeof(pfs));
91317dd83bcSMatthew Dillon 	bzero(&mrec_tmp, sizeof(mrec_tmp));
914d4e5b69bSMatthew Dillon 	pfs.pfs_id = pfs_id;
91517dd83bcSMatthew Dillon 	pfs.ondisk = &mrec_tmp.pfs.pfsd;
91617dd83bcSMatthew Dillon 	pfs.bytes = sizeof(mrec_tmp.pfs.pfsd);
91734ebae70SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
9189dc76cb1SMatthew Dillon 		fprintf(stderr, "Mirror-read: not a HAMMER fs/pseudofs!\n");
91934ebae70SMatthew Dillon 		exit(1);
92034ebae70SMatthew Dillon 	}
92134ebae70SMatthew Dillon 	if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
9229dc76cb1SMatthew Dillon 		fprintf(stderr, "Mirror-read: HAMMER pfs version mismatch!\n");
92334ebae70SMatthew Dillon 		exit(1);
92434ebae70SMatthew Dillon 	}
92534ebae70SMatthew Dillon 
92634ebae70SMatthew Dillon 	/*
92734ebae70SMatthew Dillon 	 * sync_beg_tid - lowest TID on source after which a full history
92834ebae70SMatthew Dillon 	 *	 	  is available.
92934ebae70SMatthew Dillon 	 *
93034ebae70SMatthew Dillon 	 * sync_end_tid - highest fully synchronized TID from source.
93134ebae70SMatthew Dillon 	 */
93217dd83bcSMatthew Dillon 	if (tid_begp && *tid_begp < mrec_tmp.pfs.pfsd.sync_beg_tid)
93317dd83bcSMatthew Dillon 		*tid_begp = mrec_tmp.pfs.pfsd.sync_beg_tid;
934d4e5b69bSMatthew Dillon 	if (tid_endp)
93517dd83bcSMatthew Dillon 		*tid_endp = mrec_tmp.pfs.pfsd.sync_end_tid;
93617dd83bcSMatthew Dillon 	mrec_tmp.pfs.version = pfs.version;
937243ca327SMatthew Dillon 	write_mrecord(fdout, HAMMER_MREC_TYPE_PFSD,
93817dd83bcSMatthew Dillon 		      &mrec_tmp, sizeof(mrec_tmp.pfs));
93934ebae70SMatthew Dillon }
94034ebae70SMatthew Dillon 
94134ebae70SMatthew Dillon /*
94234ebae70SMatthew Dillon  * Validate the pfs information from the originating filesystem
94334ebae70SMatthew Dillon  * against the target filesystem.  shared_uuid must match.
944*48eadef9SMatthew Dillon  *
945*48eadef9SMatthew Dillon  * return -1 if we got a TERM record
94634ebae70SMatthew Dillon  */
947*48eadef9SMatthew Dillon static int
948d4e5b69bSMatthew Dillon validate_mrec_header(int fd, int fdin, int is_target, int pfs_id,
949*48eadef9SMatthew Dillon 		     struct hammer_ioc_mrecord_head *pickup,
95034ebae70SMatthew Dillon 		     hammer_tid_t *tid_begp, hammer_tid_t *tid_endp)
95134ebae70SMatthew Dillon {
95234ebae70SMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
95334ebae70SMatthew Dillon 	struct hammer_pseudofs_data pfsd;
95417dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
955243ca327SMatthew Dillon 	int error;
95634ebae70SMatthew Dillon 
95734ebae70SMatthew Dillon 	/*
95834ebae70SMatthew Dillon 	 * Get the PFSD info from the target filesystem.
95934ebae70SMatthew Dillon 	 */
96034ebae70SMatthew Dillon 	bzero(&pfs, sizeof(pfs));
96134ebae70SMatthew Dillon 	bzero(&pfsd, sizeof(pfsd));
962d4e5b69bSMatthew Dillon 	pfs.pfs_id = pfs_id;
96334ebae70SMatthew Dillon 	pfs.ondisk = &pfsd;
96434ebae70SMatthew Dillon 	pfs.bytes = sizeof(pfsd);
96534ebae70SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
96634ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: not a HAMMER fs/pseudofs!\n");
96734ebae70SMatthew Dillon 		exit(1);
96834ebae70SMatthew Dillon 	}
96934ebae70SMatthew Dillon 	if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
97034ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: HAMMER pfs version mismatch!\n");
97134ebae70SMatthew Dillon 		exit(1);
97234ebae70SMatthew Dillon 	}
97334ebae70SMatthew Dillon 
974*48eadef9SMatthew Dillon 	mrec = read_mrecord(fdin, &error, pickup);
975243ca327SMatthew Dillon 	if (mrec == NULL) {
976243ca327SMatthew Dillon 		if (error == 0)
977243ca327SMatthew Dillon 			fprintf(stderr, "validate_mrec_header: short read\n");
97834ebae70SMatthew Dillon 		exit(1);
97934ebae70SMatthew Dillon 	}
980*48eadef9SMatthew Dillon 	if (mrec->head.type == HAMMER_MREC_TYPE_TERM) {
981*48eadef9SMatthew Dillon 		free(mrec);
982*48eadef9SMatthew Dillon 		return(-1);
983*48eadef9SMatthew Dillon 	}
984*48eadef9SMatthew Dillon 
98517dd83bcSMatthew Dillon 	if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) {
986243ca327SMatthew Dillon 		fprintf(stderr, "validate_mrec_header: did not get expected "
987243ca327SMatthew Dillon 				"PFSD record type\n");
98834ebae70SMatthew Dillon 		exit(1);
98934ebae70SMatthew Dillon 	}
99017dd83bcSMatthew Dillon 	if (mrec->head.rec_size != sizeof(mrec->pfs)) {
991243ca327SMatthew Dillon 		fprintf(stderr, "validate_mrec_header: unexpected payload "
992243ca327SMatthew Dillon 				"size\n");
99334ebae70SMatthew Dillon 		exit(1);
99434ebae70SMatthew Dillon 	}
99517dd83bcSMatthew Dillon 	if (mrec->pfs.version != pfs.version) {
996243ca327SMatthew Dillon 		fprintf(stderr, "validate_mrec_header: Version mismatch\n");
99734ebae70SMatthew Dillon 		exit(1);
99834ebae70SMatthew Dillon 	}
99934ebae70SMatthew Dillon 
100034ebae70SMatthew Dillon 	/*
100134ebae70SMatthew Dillon 	 * Whew.  Ok, is the read PFS info compatible with the target?
100234ebae70SMatthew Dillon 	 */
100317dd83bcSMatthew Dillon 	if (bcmp(&mrec->pfs.pfsd.shared_uuid, &pfsd.shared_uuid,
100417dd83bcSMatthew Dillon 		 sizeof(pfsd.shared_uuid)) != 0) {
100517dd83bcSMatthew Dillon 		fprintf(stderr,
100617dd83bcSMatthew Dillon 			"mirror-write: source and target have "
1007f265b84fSSascha Wildner 			"different shared-uuid's!\n");
100834ebae70SMatthew Dillon 		exit(1);
100934ebae70SMatthew Dillon 	}
1010d4e5b69bSMatthew Dillon 	if (is_target &&
1011d4e5b69bSMatthew Dillon 	    (pfsd.mirror_flags & HAMMER_PFSD_SLAVE) == 0) {
101234ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: target must be in slave mode\n");
101334ebae70SMatthew Dillon 		exit(1);
101434ebae70SMatthew Dillon 	}
1015d4e5b69bSMatthew Dillon 	if (tid_begp)
101617dd83bcSMatthew Dillon 		*tid_begp = mrec->pfs.pfsd.sync_beg_tid;
1017d4e5b69bSMatthew Dillon 	if (tid_endp)
101817dd83bcSMatthew Dillon 		*tid_endp = mrec->pfs.pfsd.sync_end_tid;
1019243ca327SMatthew Dillon 	free(mrec);
1020*48eadef9SMatthew Dillon 	return(0);
102134ebae70SMatthew Dillon }
102234ebae70SMatthew Dillon 
102334ebae70SMatthew Dillon static void
1024d4e5b69bSMatthew Dillon update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id)
102534ebae70SMatthew Dillon {
1026243ca327SMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
1027243ca327SMatthew Dillon 	struct hammer_pseudofs_data pfsd;
102834ebae70SMatthew Dillon 
1029243ca327SMatthew Dillon 	bzero(&pfs, sizeof(pfs));
1030243ca327SMatthew Dillon 	bzero(&pfsd, sizeof(pfsd));
1031d4e5b69bSMatthew Dillon 	pfs.pfs_id = pfs_id;
1032243ca327SMatthew Dillon 	pfs.ondisk = &pfsd;
1033243ca327SMatthew Dillon 	pfs.bytes = sizeof(pfsd);
1034243ca327SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
1035243ca327SMatthew Dillon 		perror("update_pfs_snapshot (read)");
1036243ca327SMatthew Dillon 		exit(1);
103734ebae70SMatthew Dillon 	}
10389c67b4d2SMatthew Dillon 	if (pfsd.sync_end_tid != snapshot_tid) {
1039ddc8e722SMatthew Dillon 		pfsd.sync_end_tid = snapshot_tid;
1040243ca327SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) != 0) {
1041243ca327SMatthew Dillon 			perror("update_pfs_snapshot (rewrite)");
1042243ca327SMatthew Dillon 			exit(1);
104334ebae70SMatthew Dillon 		}
1044*48eadef9SMatthew Dillon 		if (VerboseOpt >= 2) {
10459dc76cb1SMatthew Dillon 			fprintf(stderr,
10469dc76cb1SMatthew Dillon 				"Mirror-write: Completed, updated snapshot "
10479dc76cb1SMatthew Dillon 				"to %016llx\n",
10489dc76cb1SMatthew Dillon 				snapshot_tid);
1049243ca327SMatthew Dillon 		}
10509c67b4d2SMatthew Dillon 	}
1051*48eadef9SMatthew Dillon }
1052243ca327SMatthew Dillon 
1053*48eadef9SMatthew Dillon /*
1054*48eadef9SMatthew Dillon  * Bandwidth-limited write in chunks
1055*48eadef9SMatthew Dillon  */
1056*48eadef9SMatthew Dillon static
1057*48eadef9SMatthew Dillon ssize_t
1058*48eadef9SMatthew Dillon writebw(int fd, const void *buf, size_t nbytes,
1059*48eadef9SMatthew Dillon 	u_int64_t *bwcount, struct timeval *tv1)
1060*48eadef9SMatthew Dillon {
1061*48eadef9SMatthew Dillon 	struct timeval tv2;
1062*48eadef9SMatthew Dillon 	size_t n;
1063*48eadef9SMatthew Dillon 	ssize_t r;
1064*48eadef9SMatthew Dillon 	ssize_t a;
1065*48eadef9SMatthew Dillon 	int usec;
1066*48eadef9SMatthew Dillon 
1067*48eadef9SMatthew Dillon 	a = 0;
1068*48eadef9SMatthew Dillon 	r = 0;
1069*48eadef9SMatthew Dillon 	while (nbytes) {
1070*48eadef9SMatthew Dillon 		if (*bwcount + nbytes > BandwidthOpt)
1071*48eadef9SMatthew Dillon 			n = BandwidthOpt - *bwcount;
1072*48eadef9SMatthew Dillon 		else
1073*48eadef9SMatthew Dillon 			n = nbytes;
1074*48eadef9SMatthew Dillon 		if (n)
1075*48eadef9SMatthew Dillon 			r = write(fd, buf, n);
1076*48eadef9SMatthew Dillon 		if (r >= 0) {
1077*48eadef9SMatthew Dillon 			a += r;
1078*48eadef9SMatthew Dillon 			nbytes -= r;
1079*48eadef9SMatthew Dillon 			buf = (const char *)buf + r;
1080*48eadef9SMatthew Dillon 		}
1081*48eadef9SMatthew Dillon 		if ((size_t)r != n)
1082*48eadef9SMatthew Dillon 			break;
1083*48eadef9SMatthew Dillon 		*bwcount += n;
1084*48eadef9SMatthew Dillon 		if (*bwcount >= BandwidthOpt) {
1085*48eadef9SMatthew Dillon 			gettimeofday(&tv2, NULL);
1086*48eadef9SMatthew Dillon 			usec = (int)(tv2.tv_sec - tv1->tv_sec) * 1000000 +
1087*48eadef9SMatthew Dillon 				(int)(tv2.tv_usec - tv1->tv_usec);
1088*48eadef9SMatthew Dillon 			if (usec >= 0 && usec < 1000000)
1089*48eadef9SMatthew Dillon 				usleep(1000000 - usec);
1090*48eadef9SMatthew Dillon 			gettimeofday(tv1, NULL);
1091*48eadef9SMatthew Dillon 			*bwcount -= BandwidthOpt;
1092*48eadef9SMatthew Dillon 		}
1093*48eadef9SMatthew Dillon 	}
1094*48eadef9SMatthew Dillon 	return(a ? a : r);
1095*48eadef9SMatthew Dillon }
109634ebae70SMatthew Dillon 
1097a7fbbf91SMatthew Dillon static void
1098a7fbbf91SMatthew Dillon mirror_usage(int code)
1099a7fbbf91SMatthew Dillon {
1100a7fbbf91SMatthew Dillon 	fprintf(stderr,
1101a7fbbf91SMatthew Dillon 		"hammer mirror-read <filesystem>\n"
1102a7fbbf91SMatthew Dillon 		"hammer mirror-write <filesystem>\n"
1103243ca327SMatthew Dillon 		"hammer mirror-dump\n"
1104a7fbbf91SMatthew Dillon 		"hammer mirror-copy [[user@]host:]fs [[user@]host:]fs\n"
1105a7fbbf91SMatthew Dillon 	);
1106a7fbbf91SMatthew Dillon 	exit(code);
1107a7fbbf91SMatthew Dillon }
1108