xref: /dflybsd-src/sbin/hammer/cmd_mirror.c (revision 9dc76cb12d9b065738b6b95f4495ac81a39170c6)
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*9dc76cb1SMatthew Dillon  * $DragonFly: src/sbin/hammer/cmd_mirror.c,v 1.9 2008/07/12 23:05:30 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);
49d4e5b69bSMatthew Dillon static void validate_mrec_header(int fd, int fdin, int is_target, int pfs_id,
5034ebae70SMatthew Dillon 			 hammer_tid_t *tid_begp, hammer_tid_t *tid_endp);
51d4e5b69bSMatthew Dillon static void update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id);
52a7fbbf91SMatthew Dillon static void mirror_usage(int code);
53a7fbbf91SMatthew Dillon 
5417dd83bcSMatthew Dillon /*
5517dd83bcSMatthew Dillon  * Generate a mirroring data stream from the specific source over the
5617dd83bcSMatthew Dillon  * entire key range, but restricted to the specified transaction range.
5717dd83bcSMatthew Dillon  *
5817dd83bcSMatthew Dillon  * The HAMMER VFS does most of the work, we add a few new mrecord
5917dd83bcSMatthew Dillon  * types to negotiate the TID ranges and verify that the entire
6017dd83bcSMatthew Dillon  * stream made it to the destination.
6117dd83bcSMatthew Dillon  */
62a7fbbf91SMatthew Dillon void
63a7fbbf91SMatthew Dillon hammer_cmd_mirror_read(char **av, int ac)
64a7fbbf91SMatthew Dillon {
65a7fbbf91SMatthew Dillon 	struct hammer_ioc_mirror_rw mirror;
66d4e5b69bSMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
6717dd83bcSMatthew Dillon 	union hammer_ioc_mrecord_any mrec_tmp;
6817dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
69243ca327SMatthew Dillon 	hammer_tid_t sync_tid;
70a7fbbf91SMatthew Dillon 	const char *filesystem;
71a7fbbf91SMatthew Dillon 	char *buf = malloc(SERIALBUF_SIZE);
72243ca327SMatthew Dillon 	int interrupted = 0;
73243ca327SMatthew Dillon 	int error;
74a7fbbf91SMatthew Dillon 	int fd;
75243ca327SMatthew Dillon 	int n;
76243ca327SMatthew Dillon 	time_t base_t = time(NULL);
77a7fbbf91SMatthew Dillon 
78a7fbbf91SMatthew Dillon 	if (ac > 2)
79a7fbbf91SMatthew Dillon 		mirror_usage(1);
80a7fbbf91SMatthew Dillon 	filesystem = av[0];
81a7fbbf91SMatthew Dillon 
82a7fbbf91SMatthew Dillon 	bzero(&mirror, sizeof(mirror));
83a7fbbf91SMatthew Dillon 	hammer_key_beg_init(&mirror.key_beg);
84a7fbbf91SMatthew Dillon 	hammer_key_end_init(&mirror.key_end);
85a7fbbf91SMatthew Dillon 
86d4e5b69bSMatthew Dillon 	fd = getpfs(&pfs, filesystem);
87a7fbbf91SMatthew Dillon 
88243ca327SMatthew Dillon 	/*
89d4e5b69bSMatthew Dillon 	 * In 2-way mode the target will send us a PFS info packet
90d4e5b69bSMatthew Dillon 	 * first.  Use the target's current snapshot TID as our default
91d4e5b69bSMatthew Dillon 	 * begin TID.
92243ca327SMatthew Dillon 	 */
93d4e5b69bSMatthew Dillon 	mirror.tid_beg = 0;
94d4e5b69bSMatthew Dillon 	if (TwoWayPipeOpt)
95d4e5b69bSMatthew Dillon 		validate_mrec_header(fd, 0, 0, pfs.pfs_id,
96d4e5b69bSMatthew Dillon 				     NULL, &mirror.tid_beg);
97d4e5b69bSMatthew Dillon 
98d4e5b69bSMatthew Dillon 	/*
99d4e5b69bSMatthew Dillon 	 * Write out the PFS header, tid_beg will be updated if our PFS
100d4e5b69bSMatthew Dillon 	 * has a larger begin sync.  tid_end is set to the latest source
101d4e5b69bSMatthew Dillon 	 * TID whos flush cycle has completed.
102d4e5b69bSMatthew Dillon 	 */
103d4e5b69bSMatthew Dillon 	generate_mrec_header(fd, 1, pfs.pfs_id,
104d4e5b69bSMatthew Dillon 			     &mirror.tid_beg, &mirror.tid_end);
105d4e5b69bSMatthew Dillon 
106d4e5b69bSMatthew Dillon 	/*
107d4e5b69bSMatthew Dillon 	 * A cycle file overrides the beginning TID
108d4e5b69bSMatthew Dillon 	 */
109243ca327SMatthew Dillon 	hammer_get_cycle(&mirror.key_beg, &mirror.tid_beg);
11034ebae70SMatthew Dillon 
11117dd83bcSMatthew Dillon 	if (ac == 2)
11217dd83bcSMatthew Dillon 		mirror.tid_beg = strtoull(av[1], NULL, 0);
11317dd83bcSMatthew Dillon 
114*9dc76cb1SMatthew Dillon 	fprintf(stderr, "Mirror-read: Mirror from %016llx to %016llx\n",
115243ca327SMatthew Dillon 		mirror.tid_beg, mirror.tid_end);
116243ca327SMatthew Dillon 	if (mirror.key_beg.obj_id != (int64_t)HAMMER_MIN_OBJID) {
117*9dc76cb1SMatthew Dillon 		fprintf(stderr, "Mirror-read: Resuming at object %016llx\n",
118243ca327SMatthew Dillon 			mirror.key_beg.obj_id);
119243ca327SMatthew Dillon 	}
120243ca327SMatthew Dillon 
121243ca327SMatthew Dillon 	/*
122*9dc76cb1SMatthew Dillon 	 * Nothing to do if begin equals end.
123*9dc76cb1SMatthew Dillon 	 */
124*9dc76cb1SMatthew Dillon 	if (mirror.tid_beg == mirror.tid_end) {
125*9dc76cb1SMatthew Dillon 		fprintf(stderr, "Mirror-read: No work to do, stopping\n");
126*9dc76cb1SMatthew Dillon 		write_mrecord(1, HAMMER_MREC_TYPE_TERM,
127*9dc76cb1SMatthew Dillon 			      &mrec_tmp, sizeof(mrec_tmp.sync));
128*9dc76cb1SMatthew Dillon 		goto done;
129*9dc76cb1SMatthew Dillon 	}
130*9dc76cb1SMatthew Dillon 
131*9dc76cb1SMatthew Dillon 	/*
132243ca327SMatthew Dillon 	 * Write out bulk records
133243ca327SMatthew Dillon 	 */
134a7fbbf91SMatthew Dillon 	mirror.ubuf = buf;
135a7fbbf91SMatthew Dillon 	mirror.size = SERIALBUF_SIZE;
136a7fbbf91SMatthew Dillon 
137a7fbbf91SMatthew Dillon 	do {
138a7fbbf91SMatthew Dillon 		mirror.count = 0;
139d4e5b69bSMatthew Dillon 		mirror.pfs_id = pfs.pfs_id;
140d4e5b69bSMatthew Dillon 		mirror.shared_uuid = pfs.ondisk->shared_uuid;
141a7fbbf91SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) {
142a7fbbf91SMatthew Dillon 			fprintf(stderr, "Mirror-read %s failed: %s\n",
143a7fbbf91SMatthew Dillon 				filesystem, strerror(errno));
144a7fbbf91SMatthew Dillon 			exit(1);
145a7fbbf91SMatthew Dillon 		}
1469c67b4d2SMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) {
1479c67b4d2SMatthew Dillon 			fprintf(stderr,
1489c67b4d2SMatthew Dillon 				"Mirror-read %s fatal error %d\n",
1499c67b4d2SMatthew Dillon 				filesystem, mirror.head.error);
1509c67b4d2SMatthew Dillon 			exit(1);
1519c67b4d2SMatthew Dillon 		}
152243ca327SMatthew Dillon 		if (mirror.count) {
153243ca327SMatthew Dillon 			n = write(1, mirror.ubuf, mirror.count);
154243ca327SMatthew Dillon 			if (n != mirror.count) {
155243ca327SMatthew Dillon 				fprintf(stderr, "Mirror-read %s failed: "
156243ca327SMatthew Dillon 						"short write\n",
157243ca327SMatthew Dillon 				filesystem);
158243ca327SMatthew Dillon 				exit(1);
159243ca327SMatthew Dillon 			}
160a7fbbf91SMatthew Dillon 		}
161a7fbbf91SMatthew Dillon 		mirror.key_beg = mirror.key_cur;
162243ca327SMatthew Dillon 		if (TimeoutOpt &&
163243ca327SMatthew Dillon 		    (unsigned)(time(NULL) - base_t) > (unsigned)TimeoutOpt) {
164243ca327SMatthew Dillon 			fprintf(stderr,
165243ca327SMatthew Dillon 				"Mirror-read %s interrupted by timer at"
166243ca327SMatthew Dillon 				" %016llx\n",
167243ca327SMatthew Dillon 				filesystem,
168243ca327SMatthew Dillon 				mirror.key_cur.obj_id);
169243ca327SMatthew Dillon 			interrupted = 1;
170243ca327SMatthew Dillon 			break;
171243ca327SMatthew Dillon 		}
172a7fbbf91SMatthew Dillon 	} while (mirror.count != 0);
173a7fbbf91SMatthew Dillon 
174243ca327SMatthew Dillon 	/*
175243ca327SMatthew Dillon 	 * Write out the termination sync record
176243ca327SMatthew Dillon 	 */
17717dd83bcSMatthew Dillon 	write_mrecord(1, HAMMER_MREC_TYPE_SYNC,
17817dd83bcSMatthew Dillon 		      &mrec_tmp, sizeof(mrec_tmp.sync));
17934ebae70SMatthew Dillon 
180243ca327SMatthew Dillon 	/*
181243ca327SMatthew Dillon 	 * If the -2 option was given (automatic when doing mirror-copy),
182243ca327SMatthew Dillon 	 * a two-way pipe is assumed and we expect a response mrec from
183243ca327SMatthew Dillon 	 * the target.
184243ca327SMatthew Dillon 	 */
185243ca327SMatthew Dillon 	if (TwoWayPipeOpt) {
186243ca327SMatthew Dillon 		mrec = read_mrecord(0, &error, NULL);
18717dd83bcSMatthew Dillon 		if (mrec == NULL ||
18817dd83bcSMatthew Dillon 		    mrec->head.type != HAMMER_MREC_TYPE_UPDATE ||
18917dd83bcSMatthew Dillon 		    mrec->head.rec_size != sizeof(mrec->update)) {
190243ca327SMatthew Dillon 			fprintf(stderr, "mirror_read: Did not get final "
191243ca327SMatthew Dillon 					"acknowledgement packet from target\n");
192243ca327SMatthew Dillon 			exit(1);
193243ca327SMatthew Dillon 		}
194243ca327SMatthew Dillon 		if (interrupted) {
195243ca327SMatthew Dillon 			if (CyclePath) {
196243ca327SMatthew Dillon 				hammer_set_cycle(&mirror.key_cur, mirror.tid_beg);
197243ca327SMatthew Dillon 				fprintf(stderr, "Cyclefile %s updated for continuation\n", CyclePath);
198243ca327SMatthew Dillon 			}
199243ca327SMatthew Dillon 		} else {
20017dd83bcSMatthew Dillon 			sync_tid = mrec->update.tid;
201243ca327SMatthew Dillon 			if (CyclePath) {
202243ca327SMatthew Dillon 				hammer_key_beg_init(&mirror.key_beg);
203243ca327SMatthew Dillon 				hammer_set_cycle(&mirror.key_beg, sync_tid);
204243ca327SMatthew Dillon 				fprintf(stderr, "Cyclefile %s updated to 0x%016llx\n",
205243ca327SMatthew Dillon 					CyclePath, sync_tid);
206243ca327SMatthew Dillon 			}
207243ca327SMatthew Dillon 		}
208243ca327SMatthew Dillon 	} else if (CyclePath) {
209243ca327SMatthew Dillon 		/* NOTE! mirror.tid_beg cannot be updated */
210243ca327SMatthew Dillon 		fprintf(stderr, "Warning: cycle file (-c option) cannot be "
211243ca327SMatthew Dillon 				"fully updated unless you use mirror-copy\n");
212243ca327SMatthew Dillon 		hammer_set_cycle(&mirror.key_beg, mirror.tid_beg);
213243ca327SMatthew Dillon 	}
214*9dc76cb1SMatthew Dillon done:
215a7fbbf91SMatthew Dillon 	fprintf(stderr, "Mirror-read %s succeeded\n", filesystem);
216a7fbbf91SMatthew Dillon }
217a7fbbf91SMatthew Dillon 
21817dd83bcSMatthew Dillon /*
21917dd83bcSMatthew Dillon  * Pipe the mirroring data stream on stdin to the HAMMER VFS, adding
22017dd83bcSMatthew Dillon  * some additional packet types to negotiate TID ranges and to verify
22117dd83bcSMatthew Dillon  * completion.  The HAMMER VFS does most of the work.
22217dd83bcSMatthew Dillon  *
22317dd83bcSMatthew Dillon  * It is important to note that the mirror.key_{beg,end} range must
22417dd83bcSMatthew Dillon  * match the ranged used by the original.  For now both sides use
22517dd83bcSMatthew Dillon  * range the entire key space.
22617dd83bcSMatthew Dillon  *
22717dd83bcSMatthew Dillon  * It is even more important that the records in the stream conform
22817dd83bcSMatthew Dillon  * to the TID range also supplied in the stream.  The HAMMER VFS will
22917dd83bcSMatthew Dillon  * use the REC, PASS, and SKIP record types to track the portions of
23017dd83bcSMatthew Dillon  * the B-Tree being scanned in order to be able to proactively delete
23117dd83bcSMatthew Dillon  * records on the target within those active areas that are not mentioned
23217dd83bcSMatthew Dillon  * by the source.
23317dd83bcSMatthew Dillon  *
23417dd83bcSMatthew Dillon  * The mirror.key_cur field is used by the VFS to do this tracking.  It
23517dd83bcSMatthew Dillon  * must be initialized to key_beg but then is persistently updated by
23617dd83bcSMatthew Dillon  * the HAMMER VFS on each successive ioctl() call.  If you blow up this
23717dd83bcSMatthew Dillon  * field you will blow up the mirror target, possibly to the point of
23817dd83bcSMatthew Dillon  * deleting everything.  As a safety measure the HAMMER VFS simply marks
23917dd83bcSMatthew Dillon  * the records that the source has destroyed as deleted on the target,
24017dd83bcSMatthew Dillon  * and normal pruning operations will deal with their final disposition
24117dd83bcSMatthew Dillon  * at some later time.
24217dd83bcSMatthew Dillon  */
243a7fbbf91SMatthew Dillon void
244a7fbbf91SMatthew Dillon hammer_cmd_mirror_write(char **av, int ac)
245a7fbbf91SMatthew Dillon {
246a7fbbf91SMatthew Dillon 	struct hammer_ioc_mirror_rw mirror;
247a7fbbf91SMatthew Dillon 	const char *filesystem;
248a7fbbf91SMatthew Dillon 	char *buf = malloc(SERIALBUF_SIZE);
249d4e5b69bSMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
25017dd83bcSMatthew Dillon 	struct hammer_ioc_mrecord_head pickup;
251243ca327SMatthew Dillon 	struct hammer_ioc_synctid synctid;
25217dd83bcSMatthew Dillon 	union hammer_ioc_mrecord_any mrec_tmp;
25317dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
254243ca327SMatthew Dillon 	int error;
255243ca327SMatthew Dillon 	int fd;
256a7fbbf91SMatthew Dillon 
257a7fbbf91SMatthew Dillon 	if (ac > 2)
258a7fbbf91SMatthew Dillon 		mirror_usage(1);
259a7fbbf91SMatthew Dillon 	filesystem = av[0];
260a7fbbf91SMatthew Dillon 
261a7fbbf91SMatthew Dillon 	bzero(&mirror, sizeof(mirror));
262a7fbbf91SMatthew Dillon 	hammer_key_beg_init(&mirror.key_beg);
263a7fbbf91SMatthew Dillon 	hammer_key_end_init(&mirror.key_end);
26417dd83bcSMatthew Dillon 	mirror.key_end = mirror.key_beg;
265a7fbbf91SMatthew Dillon 
266d4e5b69bSMatthew Dillon 	fd = getpfs(&pfs, filesystem);
267a7fbbf91SMatthew Dillon 
268243ca327SMatthew Dillon 	/*
269d4e5b69bSMatthew Dillon 	 * In two-way mode the target writes out a PFS packet first.
270d4e5b69bSMatthew Dillon 	 * The source uses our tid_end as its tid_beg by default,
271d4e5b69bSMatthew Dillon 	 * picking up where it left off.
272243ca327SMatthew Dillon 	 */
273d4e5b69bSMatthew Dillon 	mirror.tid_beg = 0;
274d4e5b69bSMatthew Dillon 	if (TwoWayPipeOpt) {
275d4e5b69bSMatthew Dillon 		generate_mrec_header(fd, 1, pfs.pfs_id,
276d4e5b69bSMatthew Dillon 				     &mirror.tid_beg, &mirror.tid_end);
277d4e5b69bSMatthew Dillon 	}
278d4e5b69bSMatthew Dillon 
279d4e5b69bSMatthew Dillon 	/*
28017dd83bcSMatthew Dillon 	 * Read and process the PFS header.  The source informs us of
28117dd83bcSMatthew Dillon 	 * the TID range the stream represents.
282d4e5b69bSMatthew Dillon 	 */
283d4e5b69bSMatthew Dillon 	validate_mrec_header(fd, 0, 1, pfs.pfs_id,
284d4e5b69bSMatthew Dillon 			     &mirror.tid_beg, &mirror.tid_end);
28534ebae70SMatthew Dillon 
286a7fbbf91SMatthew Dillon 	mirror.ubuf = buf;
287a7fbbf91SMatthew Dillon 	mirror.size = SERIALBUF_SIZE;
288a7fbbf91SMatthew Dillon 
289a7fbbf91SMatthew Dillon 	pickup.signature = 0;
290243ca327SMatthew Dillon 	pickup.type = 0;
291a7fbbf91SMatthew Dillon 
292243ca327SMatthew Dillon 	/*
29317dd83bcSMatthew Dillon 	 * Read and process bulk records (REC, PASS, and SKIP types).
29417dd83bcSMatthew Dillon 	 *
29517dd83bcSMatthew Dillon 	 * On your life, do NOT mess with mirror.key_cur or your mirror
29617dd83bcSMatthew Dillon 	 * target may become history.
297243ca327SMatthew Dillon 	 */
298a7fbbf91SMatthew Dillon 	for (;;) {
299a7fbbf91SMatthew Dillon 		mirror.count = 0;
300d4e5b69bSMatthew Dillon 		mirror.pfs_id = pfs.pfs_id;
301d4e5b69bSMatthew Dillon 		mirror.shared_uuid = pfs.ondisk->shared_uuid;
302a7fbbf91SMatthew Dillon 		mirror.size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup);
303a7fbbf91SMatthew Dillon 		if (mirror.size <= 0)
304a7fbbf91SMatthew Dillon 			break;
305a7fbbf91SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_MIRROR_WRITE, &mirror) < 0) {
306a7fbbf91SMatthew Dillon 			fprintf(stderr, "Mirror-write %s failed: %s\n",
307a7fbbf91SMatthew Dillon 				filesystem, strerror(errno));
308a7fbbf91SMatthew Dillon 			exit(1);
309a7fbbf91SMatthew Dillon 		}
31017dd83bcSMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) {
31117dd83bcSMatthew Dillon 			fprintf(stderr,
31217dd83bcSMatthew Dillon 				"Mirror-write %s fatal error %d\n",
31317dd83bcSMatthew Dillon 				filesystem, mirror.head.error);
31417dd83bcSMatthew Dillon 			exit(1);
31517dd83bcSMatthew Dillon 		}
316243ca327SMatthew Dillon #if 0
317a7fbbf91SMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_INTR) {
318a7fbbf91SMatthew Dillon 			fprintf(stderr,
319a7fbbf91SMatthew Dillon 				"Mirror-write %s interrupted by timer at"
320243ca327SMatthew Dillon 				" %016llx\n",
321a7fbbf91SMatthew Dillon 				filesystem,
322243ca327SMatthew Dillon 				mirror.key_cur.obj_id);
323a7fbbf91SMatthew Dillon 			exit(0);
324a7fbbf91SMatthew Dillon 		}
325243ca327SMatthew Dillon #endif
326a7fbbf91SMatthew Dillon 	}
327243ca327SMatthew Dillon 
328243ca327SMatthew Dillon 	/*
329243ca327SMatthew Dillon 	 * Read and process the termination sync record.
330243ca327SMatthew Dillon 	 */
331243ca327SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
332*9dc76cb1SMatthew Dillon 
333*9dc76cb1SMatthew Dillon 	if (mrec && mrec->head.type == HAMMER_MREC_TYPE_TERM) {
334*9dc76cb1SMatthew Dillon 		fprintf(stderr, "Mirror-write: No work to do, stopping\n");
335*9dc76cb1SMatthew Dillon 		return;
336*9dc76cb1SMatthew Dillon 	}
337*9dc76cb1SMatthew Dillon 
33817dd83bcSMatthew Dillon 	if (mrec == NULL ||
33917dd83bcSMatthew Dillon 	    mrec->head.type != HAMMER_MREC_TYPE_SYNC ||
34017dd83bcSMatthew Dillon 	    mrec->head.rec_size != sizeof(mrec->sync)) {
341243ca327SMatthew Dillon 		fprintf(stderr, "Mirror-write %s: Did not get termination "
34217dd83bcSMatthew Dillon 				"sync record, or rec_size is wrong rt=%d\n",
34317dd83bcSMatthew Dillon 				filesystem, mrec->head.type);
344243ca327SMatthew Dillon 	}
34517dd83bcSMatthew Dillon 	free(mrec);
34617dd83bcSMatthew Dillon 	mrec = NULL;
347243ca327SMatthew Dillon 
348243ca327SMatthew Dillon 	/*
349243ca327SMatthew Dillon 	 * Update the PFS info on the target so the user has visibility
350243ca327SMatthew Dillon 	 * into the new snapshot.
351243ca327SMatthew Dillon 	 */
352d4e5b69bSMatthew Dillon 	update_pfs_snapshot(fd, mirror.tid_end, pfs.pfs_id);
353243ca327SMatthew Dillon 
354243ca327SMatthew Dillon 	/*
355243ca327SMatthew Dillon 	 * Sync the target filesystem
356243ca327SMatthew Dillon 	 */
357243ca327SMatthew Dillon 	bzero(&synctid, sizeof(synctid));
358243ca327SMatthew Dillon 	synctid.op = HAMMER_SYNCTID_SYNC2;
359243ca327SMatthew Dillon 	ioctl(fd, HAMMERIOC_SYNCTID, &synctid);
360243ca327SMatthew Dillon 
361243ca327SMatthew Dillon 	fprintf(stderr, "Mirror-write %s: succeeded\n", filesystem);
362243ca327SMatthew Dillon 
363243ca327SMatthew Dillon 	/*
364243ca327SMatthew Dillon 	 * Report back to the originator.
365243ca327SMatthew Dillon 	 */
366243ca327SMatthew Dillon 	if (TwoWayPipeOpt) {
36717dd83bcSMatthew Dillon 		mrec_tmp.update.tid = mirror.tid_end;
368243ca327SMatthew Dillon 		write_mrecord(1, HAMMER_MREC_TYPE_UPDATE,
36917dd83bcSMatthew Dillon 			      &mrec_tmp, sizeof(mrec_tmp.update));
370243ca327SMatthew Dillon 	} else {
371243ca327SMatthew Dillon 		printf("Source can update synctid to 0x%016llx\n",
372243ca327SMatthew Dillon 		       mirror.tid_end);
373243ca327SMatthew Dillon 	}
374243ca327SMatthew Dillon }
375243ca327SMatthew Dillon 
376243ca327SMatthew Dillon void
377243ca327SMatthew Dillon hammer_cmd_mirror_dump(void)
378243ca327SMatthew Dillon {
379243ca327SMatthew Dillon 	char *buf = malloc(SERIALBUF_SIZE);
38017dd83bcSMatthew Dillon 	struct hammer_ioc_mrecord_head pickup;
38117dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
382243ca327SMatthew Dillon 	int error;
383243ca327SMatthew Dillon 	int size;
38417dd83bcSMatthew Dillon 	int offset;
38517dd83bcSMatthew Dillon 	int bytes;
386243ca327SMatthew Dillon 
387243ca327SMatthew Dillon 	/*
388243ca327SMatthew Dillon 	 * Read and process the PFS header
389243ca327SMatthew Dillon 	 */
390243ca327SMatthew Dillon 	pickup.signature = 0;
391243ca327SMatthew Dillon 	pickup.type = 0;
392243ca327SMatthew Dillon 
393243ca327SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
394243ca327SMatthew Dillon 
395243ca327SMatthew Dillon 	/*
396243ca327SMatthew Dillon 	 * Read and process bulk records
397243ca327SMatthew Dillon 	 */
398243ca327SMatthew Dillon 	for (;;) {
399243ca327SMatthew Dillon 		size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup);
400243ca327SMatthew Dillon 		if (size <= 0)
401243ca327SMatthew Dillon 			break;
40217dd83bcSMatthew Dillon 		offset = 0;
40317dd83bcSMatthew Dillon 		while (offset < size) {
40417dd83bcSMatthew Dillon 			mrec = (void *)((char *)buf + offset);
40517dd83bcSMatthew Dillon 			bytes = HAMMER_HEAD_DOALIGN(mrec->head.rec_size);
40617dd83bcSMatthew Dillon 			if (offset + bytes > size) {
40717dd83bcSMatthew Dillon 				fprintf(stderr, "Misaligned record\n");
40817dd83bcSMatthew Dillon 				exit(1);
40917dd83bcSMatthew Dillon 			}
41017dd83bcSMatthew Dillon 
41117dd83bcSMatthew Dillon 			switch(mrec->head.type) {
41217dd83bcSMatthew Dillon 			case HAMMER_MREC_TYPE_REC:
413243ca327SMatthew Dillon 				printf("Record obj=%016llx key=%016llx "
414243ca327SMatthew Dillon 				       "rt=%02x ot=%02x\n",
41517dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_id,
41617dd83bcSMatthew Dillon 					mrec->rec.leaf.base.key,
41717dd83bcSMatthew Dillon 					mrec->rec.leaf.base.rec_type,
41817dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_type);
419243ca327SMatthew Dillon 				printf("       tids %016llx:%016llx data=%d\n",
42017dd83bcSMatthew Dillon 					mrec->rec.leaf.base.create_tid,
42117dd83bcSMatthew Dillon 					mrec->rec.leaf.base.delete_tid,
42217dd83bcSMatthew Dillon 					mrec->rec.leaf.data_len);
42317dd83bcSMatthew Dillon 				break;
42417dd83bcSMatthew Dillon 			case HAMMER_MREC_TYPE_PASS:
42517dd83bcSMatthew Dillon 				printf("Pass   obj=%016llx key=%016llx "
42617dd83bcSMatthew Dillon 				       "rt=%02x ot=%02x\n",
42717dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_id,
42817dd83bcSMatthew Dillon 					mrec->rec.leaf.base.key,
42917dd83bcSMatthew Dillon 					mrec->rec.leaf.base.rec_type,
43017dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_type);
43117dd83bcSMatthew Dillon 				printf("       tids %016llx:%016llx data=%d\n",
43217dd83bcSMatthew Dillon 					mrec->rec.leaf.base.create_tid,
43317dd83bcSMatthew Dillon 					mrec->rec.leaf.base.delete_tid,
43417dd83bcSMatthew Dillon 					mrec->rec.leaf.data_len);
43517dd83bcSMatthew Dillon 				break;
43617dd83bcSMatthew Dillon 			case HAMMER_MREC_TYPE_SKIP:
43717dd83bcSMatthew Dillon 				printf("Skip   obj=%016llx key=%016llx rt=%02x to\n"
43817dd83bcSMatthew Dillon 				       "       obj=%016llx key=%016llx rt=%02x\n",
43917dd83bcSMatthew Dillon 				       mrec->skip.skip_beg.obj_id,
44017dd83bcSMatthew Dillon 				       mrec->skip.skip_beg.key,
44117dd83bcSMatthew Dillon 				       mrec->skip.skip_beg.rec_type,
44217dd83bcSMatthew Dillon 				       mrec->skip.skip_end.obj_id,
44317dd83bcSMatthew Dillon 				       mrec->skip.skip_end.key,
44417dd83bcSMatthew Dillon 				       mrec->skip.skip_end.rec_type);
44517dd83bcSMatthew Dillon 			default:
44617dd83bcSMatthew Dillon 				break;
44717dd83bcSMatthew Dillon 			}
44817dd83bcSMatthew Dillon 			offset += bytes;
449243ca327SMatthew Dillon 		}
450243ca327SMatthew Dillon 	}
451243ca327SMatthew Dillon 
452243ca327SMatthew Dillon 	/*
453243ca327SMatthew Dillon 	 * Read and process the termination sync record.
454243ca327SMatthew Dillon 	 */
455243ca327SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
45617dd83bcSMatthew Dillon 	if (mrec == NULL || mrec->head.type != HAMMER_MREC_TYPE_SYNC) {
457243ca327SMatthew Dillon 		fprintf(stderr, "Mirror-dump: Did not get termination "
458243ca327SMatthew Dillon 				"sync record\n");
459243ca327SMatthew Dillon 	}
460a7fbbf91SMatthew Dillon }
461a7fbbf91SMatthew Dillon 
462a7fbbf91SMatthew Dillon void
463a7fbbf91SMatthew Dillon hammer_cmd_mirror_copy(char **av, int ac)
464a7fbbf91SMatthew Dillon {
46534ebae70SMatthew Dillon 	pid_t pid1;
46634ebae70SMatthew Dillon 	pid_t pid2;
46734ebae70SMatthew Dillon 	int fds[2];
468243ca327SMatthew Dillon 	const char *xav[16];
469243ca327SMatthew Dillon 	char tbuf[16];
47034ebae70SMatthew Dillon 	char *ptr;
471243ca327SMatthew Dillon 	int xac;
47234ebae70SMatthew Dillon 
47334ebae70SMatthew Dillon 	if (ac != 2)
47434ebae70SMatthew Dillon 		mirror_usage(1);
47534ebae70SMatthew Dillon 
47634ebae70SMatthew Dillon 	if (pipe(fds) < 0) {
47734ebae70SMatthew Dillon 		perror("pipe");
47834ebae70SMatthew Dillon 		exit(1);
47934ebae70SMatthew Dillon 	}
48034ebae70SMatthew Dillon 
481243ca327SMatthew Dillon 	TwoWayPipeOpt = 1;
482243ca327SMatthew Dillon 
48334ebae70SMatthew Dillon 	/*
48434ebae70SMatthew Dillon 	 * Source
48534ebae70SMatthew Dillon 	 */
48634ebae70SMatthew Dillon 	if ((pid1 = fork()) == 0) {
48734ebae70SMatthew Dillon 		dup2(fds[0], 0);
48834ebae70SMatthew Dillon 		dup2(fds[0], 1);
48934ebae70SMatthew Dillon 		close(fds[0]);
49034ebae70SMatthew Dillon 		close(fds[1]);
49134ebae70SMatthew Dillon 		if ((ptr = strchr(av[0], ':')) != NULL) {
49234ebae70SMatthew Dillon 			*ptr++ = 0;
493243ca327SMatthew Dillon 			xac = 0;
494243ca327SMatthew Dillon 			xav[xac++] = "ssh";
495243ca327SMatthew Dillon 			xav[xac++] = av[0];
496243ca327SMatthew Dillon 			xav[xac++] = "hammer";
497243ca327SMatthew Dillon 			if (VerboseOpt)
498243ca327SMatthew Dillon 				xav[xac++] = "-v";
499243ca327SMatthew Dillon 			xav[xac++] = "-2";
500243ca327SMatthew Dillon 			if (TimeoutOpt) {
501243ca327SMatthew Dillon 				snprintf(tbuf, sizeof(tbuf), "%d", TimeoutOpt);
502243ca327SMatthew Dillon 				xav[xac++] = "-t";
503243ca327SMatthew Dillon 				xav[xac++] = tbuf;
504243ca327SMatthew Dillon 			}
505243ca327SMatthew Dillon 			xav[xac++] = "mirror-read";
506243ca327SMatthew Dillon 			xav[xac++] = ptr;
507243ca327SMatthew Dillon 			xav[xac++] = NULL;
508243ca327SMatthew Dillon 			execv("/usr/bin/ssh", (void *)xav);
50934ebae70SMatthew Dillon 		} else {
51034ebae70SMatthew Dillon 			hammer_cmd_mirror_read(av, 1);
511243ca327SMatthew Dillon 			fflush(stdout);
512243ca327SMatthew Dillon 			fflush(stderr);
51334ebae70SMatthew Dillon 		}
51453d93cc7SMatthew Dillon 		_exit(1);
51534ebae70SMatthew Dillon 	}
51634ebae70SMatthew Dillon 
51734ebae70SMatthew Dillon 	/*
51834ebae70SMatthew Dillon 	 * Target
51934ebae70SMatthew Dillon 	 */
52034ebae70SMatthew Dillon 	if ((pid2 = fork()) == 0) {
52134ebae70SMatthew Dillon 		dup2(fds[1], 0);
52234ebae70SMatthew Dillon 		dup2(fds[1], 1);
52334ebae70SMatthew Dillon 		close(fds[0]);
52434ebae70SMatthew Dillon 		close(fds[1]);
52534ebae70SMatthew Dillon 		if ((ptr = strchr(av[1], ':')) != NULL) {
52634ebae70SMatthew Dillon 			*ptr++ = 0;
527243ca327SMatthew Dillon 			xac = 0;
528243ca327SMatthew Dillon 			xav[xac++] = "ssh";
529243ca327SMatthew Dillon 			xav[xac++] = av[1];
530243ca327SMatthew Dillon 			xav[xac++] = "hammer";
531243ca327SMatthew Dillon 			if (VerboseOpt)
532243ca327SMatthew Dillon 				xav[xac++] = "-v";
533243ca327SMatthew Dillon 			xav[xac++] = "-2";
534243ca327SMatthew Dillon 			xav[xac++] = "mirror-write";
535243ca327SMatthew Dillon 			xav[xac++] = ptr;
536243ca327SMatthew Dillon 			xav[xac++] = NULL;
537243ca327SMatthew Dillon 			execv("/usr/bin/ssh", (void *)xav);
53834ebae70SMatthew Dillon 		} else {
53934ebae70SMatthew Dillon 			hammer_cmd_mirror_write(av + 1, 1);
540243ca327SMatthew Dillon 			fflush(stdout);
541243ca327SMatthew Dillon 			fflush(stderr);
54234ebae70SMatthew Dillon 		}
54353d93cc7SMatthew Dillon 		_exit(1);
54434ebae70SMatthew Dillon 	}
54534ebae70SMatthew Dillon 	close(fds[0]);
54634ebae70SMatthew Dillon 	close(fds[1]);
54734ebae70SMatthew Dillon 
54834ebae70SMatthew Dillon 	while (waitpid(pid1, NULL, 0) <= 0)
54934ebae70SMatthew Dillon 		;
55034ebae70SMatthew Dillon 	while (waitpid(pid2, NULL, 0) <= 0)
55134ebae70SMatthew Dillon 		;
552a7fbbf91SMatthew Dillon }
553a7fbbf91SMatthew Dillon 
554243ca327SMatthew Dillon /*
555243ca327SMatthew Dillon  * Read and return multiple mrecords
556243ca327SMatthew Dillon  */
557a7fbbf91SMatthew Dillon static int
55817dd83bcSMatthew Dillon read_mrecords(int fd, char *buf, u_int size, hammer_ioc_mrecord_head_t pickup)
559a7fbbf91SMatthew Dillon {
56017dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
561a7fbbf91SMatthew Dillon 	u_int count;
562a7fbbf91SMatthew Dillon 	size_t n;
563a7fbbf91SMatthew Dillon 	size_t i;
56417dd83bcSMatthew Dillon 	size_t bytes;
565a7fbbf91SMatthew Dillon 
566a7fbbf91SMatthew Dillon 	count = 0;
567a7fbbf91SMatthew Dillon 	while (size - count >= HAMMER_MREC_HEADSIZE) {
568a7fbbf91SMatthew Dillon 		/*
569a7fbbf91SMatthew Dillon 		 * Cached the record header in case we run out of buffer
570a7fbbf91SMatthew Dillon 		 * space.
571a7fbbf91SMatthew Dillon 		 */
57217dd83bcSMatthew Dillon 		fflush(stdout);
573a7fbbf91SMatthew Dillon 		if (pickup->signature == 0) {
574a7fbbf91SMatthew Dillon 			for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) {
575a7fbbf91SMatthew Dillon 				i = read(fd, (char *)pickup + n,
576a7fbbf91SMatthew Dillon 					 HAMMER_MREC_HEADSIZE - n);
577a7fbbf91SMatthew Dillon 				if (i <= 0)
578a7fbbf91SMatthew Dillon 					break;
579a7fbbf91SMatthew Dillon 			}
580a7fbbf91SMatthew Dillon 			if (n == 0)
581a7fbbf91SMatthew Dillon 				break;
582a7fbbf91SMatthew Dillon 			if (n != HAMMER_MREC_HEADSIZE) {
583a7fbbf91SMatthew Dillon 				fprintf(stderr, "read_mrecords: short read on pipe\n");
584a7fbbf91SMatthew Dillon 				exit(1);
585a7fbbf91SMatthew Dillon 			}
586a7fbbf91SMatthew Dillon 
587a7fbbf91SMatthew Dillon 			if (pickup->signature != HAMMER_IOC_MIRROR_SIGNATURE) {
588a7fbbf91SMatthew Dillon 				fprintf(stderr, "read_mrecords: malformed record on pipe, bad signature\n");
589a7fbbf91SMatthew Dillon 				exit(1);
590a7fbbf91SMatthew Dillon 			}
591a7fbbf91SMatthew Dillon 		}
592a7fbbf91SMatthew Dillon 		if (pickup->rec_size < HAMMER_MREC_HEADSIZE ||
59317dd83bcSMatthew Dillon 		    pickup->rec_size > sizeof(*mrec) + HAMMER_XBUFSIZE) {
594a7fbbf91SMatthew Dillon 			fprintf(stderr, "read_mrecords: malformed record on pipe, illegal rec_size\n");
595a7fbbf91SMatthew Dillon 			exit(1);
596a7fbbf91SMatthew Dillon 		}
597a7fbbf91SMatthew Dillon 
598a7fbbf91SMatthew Dillon 		/*
599a7fbbf91SMatthew Dillon 		 * Stop if we have insufficient space for the record and data.
600a7fbbf91SMatthew Dillon 		 */
60117dd83bcSMatthew Dillon 		bytes = HAMMER_HEAD_DOALIGN(pickup->rec_size);
60217dd83bcSMatthew Dillon 		if (size - count < bytes)
603a7fbbf91SMatthew Dillon 			break;
604a7fbbf91SMatthew Dillon 
605a7fbbf91SMatthew Dillon 		/*
60617dd83bcSMatthew Dillon 		 * Stop if the record type is not a REC or a SKIP (the only
60717dd83bcSMatthew Dillon 		 * two types the ioctl supports.  Other types are used only
60817dd83bcSMatthew Dillon 		 * by the userland protocol).
609243ca327SMatthew Dillon 		 */
61017dd83bcSMatthew Dillon 		if (pickup->type != HAMMER_MREC_TYPE_REC &&
61117dd83bcSMatthew Dillon 		    pickup->type != HAMMER_MREC_TYPE_SKIP &&
61217dd83bcSMatthew Dillon 		    pickup->type != HAMMER_MREC_TYPE_PASS) {
613243ca327SMatthew Dillon 			break;
61417dd83bcSMatthew Dillon 		}
615243ca327SMatthew Dillon 
616243ca327SMatthew Dillon 		/*
617a7fbbf91SMatthew Dillon 		 * Read the remainder and clear the pickup signature.
618a7fbbf91SMatthew Dillon 		 */
61917dd83bcSMatthew Dillon 		for (n = HAMMER_MREC_HEADSIZE; n < bytes; n += i) {
62017dd83bcSMatthew Dillon 			i = read(fd, buf + count + n, bytes - n);
621a7fbbf91SMatthew Dillon 			if (i <= 0)
622a7fbbf91SMatthew Dillon 				break;
623a7fbbf91SMatthew Dillon 		}
62417dd83bcSMatthew Dillon 		if (n != bytes) {
625a7fbbf91SMatthew Dillon 			fprintf(stderr, "read_mrecords: short read on pipe\n");
626a7fbbf91SMatthew Dillon 			exit(1);
627a7fbbf91SMatthew Dillon 		}
62817dd83bcSMatthew Dillon 
62917dd83bcSMatthew Dillon 		bcopy(pickup, buf + count, HAMMER_MREC_HEADSIZE);
63017dd83bcSMatthew Dillon 		pickup->signature = 0;
63117dd83bcSMatthew Dillon 		pickup->type = 0;
63217dd83bcSMatthew Dillon 		mrec = (void *)(buf + count);
63317dd83bcSMatthew Dillon 
63417dd83bcSMatthew Dillon 		/*
63517dd83bcSMatthew Dillon 		 * Validate the completed record
63617dd83bcSMatthew Dillon 		 */
63717dd83bcSMatthew Dillon 		if (mrec->head.rec_crc !=
63817dd83bcSMatthew Dillon 		    crc32((char *)mrec + HAMMER_MREC_CRCOFF,
63917dd83bcSMatthew Dillon 			  mrec->head.rec_size - HAMMER_MREC_CRCOFF)) {
64017dd83bcSMatthew Dillon 			fprintf(stderr, "read_mrecords: malformed record "
64117dd83bcSMatthew Dillon 					"on pipe, bad crc\n");
64217dd83bcSMatthew Dillon 			exit(1);
643a7fbbf91SMatthew Dillon 		}
644a7fbbf91SMatthew Dillon 
64517dd83bcSMatthew Dillon 		/*
64617dd83bcSMatthew Dillon 		 * If its a B-Tree record validate the data crc
64717dd83bcSMatthew Dillon 		 */
64817dd83bcSMatthew Dillon 		if (mrec->head.type == HAMMER_MREC_TYPE_REC) {
64917dd83bcSMatthew Dillon 			if (mrec->head.rec_size <
65017dd83bcSMatthew Dillon 			    sizeof(mrec->rec) + mrec->rec.leaf.data_len) {
65117dd83bcSMatthew Dillon 				fprintf(stderr,
65217dd83bcSMatthew Dillon 					"read_mrecords: malformed record on "
65317dd83bcSMatthew Dillon 					"pipe, illegal element data_len\n");
65417dd83bcSMatthew Dillon 				exit(1);
65517dd83bcSMatthew Dillon 			}
65617dd83bcSMatthew Dillon 			if (mrec->rec.leaf.data_len &&
65717dd83bcSMatthew Dillon 			    mrec->rec.leaf.data_offset &&
65817dd83bcSMatthew Dillon 			    hammer_crc_test_leaf(&mrec->rec + 1, &mrec->rec.leaf) == 0) {
65917dd83bcSMatthew Dillon 				fprintf(stderr,
66017dd83bcSMatthew Dillon 					"read_mrecords: data_crc did not "
66117dd83bcSMatthew Dillon 					"match data! obj=%016llx key=%016llx\n",
66217dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_id,
66317dd83bcSMatthew Dillon 					mrec->rec.leaf.base.key);
66417dd83bcSMatthew Dillon 				fprintf(stderr,
66517dd83bcSMatthew Dillon 					"continuing, but there are problems\n");
66617dd83bcSMatthew Dillon 			}
66717dd83bcSMatthew Dillon 		}
66817dd83bcSMatthew Dillon 		count += bytes;
669a7fbbf91SMatthew Dillon 	}
670a7fbbf91SMatthew Dillon 	return(count);
671a7fbbf91SMatthew Dillon }
672a7fbbf91SMatthew Dillon 
67334ebae70SMatthew Dillon /*
67417dd83bcSMatthew Dillon  * Read and return a single mrecord.
675243ca327SMatthew Dillon  */
676243ca327SMatthew Dillon static
67717dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t
67817dd83bcSMatthew Dillon read_mrecord(int fdin, int *errorp, hammer_ioc_mrecord_head_t pickup)
679243ca327SMatthew Dillon {
68017dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
68117dd83bcSMatthew Dillon 	struct hammer_ioc_mrecord_head mrechd;
682243ca327SMatthew Dillon 	size_t bytes;
683243ca327SMatthew Dillon 	size_t n;
684243ca327SMatthew Dillon 	size_t i;
685243ca327SMatthew Dillon 
686243ca327SMatthew Dillon 	if (pickup && pickup->type != 0) {
687243ca327SMatthew Dillon 		mrechd = *pickup;
688243ca327SMatthew Dillon 		pickup->signature = 0;
689243ca327SMatthew Dillon 		pickup->type = 0;
690243ca327SMatthew Dillon 		n = HAMMER_MREC_HEADSIZE;
691243ca327SMatthew Dillon 	} else {
692243ca327SMatthew Dillon 		/*
693243ca327SMatthew Dillon 		 * Read in the PFSD header from the sender.
694243ca327SMatthew Dillon 		 */
695243ca327SMatthew Dillon 		for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) {
696243ca327SMatthew Dillon 			i = read(fdin, (char *)&mrechd + n, HAMMER_MREC_HEADSIZE - n);
697243ca327SMatthew Dillon 			if (i <= 0)
698243ca327SMatthew Dillon 				break;
699243ca327SMatthew Dillon 		}
700243ca327SMatthew Dillon 		if (n == 0) {
701243ca327SMatthew Dillon 			*errorp = 0;	/* EOF */
702243ca327SMatthew Dillon 			return(NULL);
703243ca327SMatthew Dillon 		}
704243ca327SMatthew Dillon 		if (n != HAMMER_MREC_HEADSIZE) {
705243ca327SMatthew Dillon 			fprintf(stderr, "short read of mrecord header\n");
706243ca327SMatthew Dillon 			*errorp = EPIPE;
707243ca327SMatthew Dillon 			return(NULL);
708243ca327SMatthew Dillon 		}
709243ca327SMatthew Dillon 	}
710243ca327SMatthew Dillon 	if (mrechd.signature != HAMMER_IOC_MIRROR_SIGNATURE) {
711243ca327SMatthew Dillon 		fprintf(stderr, "read_mrecord: bad signature\n");
712243ca327SMatthew Dillon 		*errorp = EINVAL;
713243ca327SMatthew Dillon 		return(NULL);
714243ca327SMatthew Dillon 	}
71517dd83bcSMatthew Dillon 	bytes = HAMMER_HEAD_DOALIGN(mrechd.rec_size);
71617dd83bcSMatthew Dillon 	assert(bytes >= sizeof(mrechd));
717243ca327SMatthew Dillon 	mrec = malloc(bytes);
71817dd83bcSMatthew Dillon 	mrec->head = mrechd;
71917dd83bcSMatthew Dillon 
720243ca327SMatthew Dillon 	while (n < bytes) {
721243ca327SMatthew Dillon 		i = read(fdin, (char *)mrec + n, bytes - n);
722243ca327SMatthew Dillon 		if (i <= 0)
723243ca327SMatthew Dillon 			break;
724243ca327SMatthew Dillon 		n += i;
725243ca327SMatthew Dillon 	}
726243ca327SMatthew Dillon 	if (n != bytes) {
727243ca327SMatthew Dillon 		fprintf(stderr, "read_mrecord: short read on payload\n");
728243ca327SMatthew Dillon 		*errorp = EPIPE;
729243ca327SMatthew Dillon 		return(NULL);
730243ca327SMatthew Dillon 	}
73117dd83bcSMatthew Dillon 	if (mrec->head.rec_crc !=
73217dd83bcSMatthew Dillon 	    crc32((char *)mrec + HAMMER_MREC_CRCOFF,
73317dd83bcSMatthew Dillon 		  mrec->head.rec_size - HAMMER_MREC_CRCOFF)) {
734243ca327SMatthew Dillon 		fprintf(stderr, "read_mrecord: bad CRC\n");
735243ca327SMatthew Dillon 		*errorp = EINVAL;
736243ca327SMatthew Dillon 		return(NULL);
737243ca327SMatthew Dillon 	}
738243ca327SMatthew Dillon 	*errorp = 0;
739243ca327SMatthew Dillon 	return(mrec);
740243ca327SMatthew Dillon }
741243ca327SMatthew Dillon 
742243ca327SMatthew Dillon static
743243ca327SMatthew Dillon void
74417dd83bcSMatthew Dillon write_mrecord(int fdout, u_int32_t type, hammer_ioc_mrecord_any_t mrec,
74517dd83bcSMatthew Dillon 	      int bytes)
746243ca327SMatthew Dillon {
74717dd83bcSMatthew Dillon 	char zbuf[HAMMER_HEAD_ALIGN];
74817dd83bcSMatthew Dillon 	int pad;
749243ca327SMatthew Dillon 
75017dd83bcSMatthew Dillon 	pad = HAMMER_HEAD_DOALIGN(bytes) - bytes;
75117dd83bcSMatthew Dillon 
75217dd83bcSMatthew Dillon 	assert(bytes >= (int)sizeof(mrec->head));
75317dd83bcSMatthew Dillon 	bzero(&mrec->head, sizeof(mrec->head));
75417dd83bcSMatthew Dillon 	mrec->head.signature = HAMMER_IOC_MIRROR_SIGNATURE;
75517dd83bcSMatthew Dillon 	mrec->head.type = type;
75617dd83bcSMatthew Dillon 	mrec->head.rec_size = bytes;
75717dd83bcSMatthew Dillon 	mrec->head.rec_crc = crc32((char *)mrec + HAMMER_MREC_CRCOFF,
75817dd83bcSMatthew Dillon 				   bytes - HAMMER_MREC_CRCOFF);
75917dd83bcSMatthew Dillon 	if (write(fdout, mrec, bytes) != bytes) {
760243ca327SMatthew Dillon 		fprintf(stderr, "write_mrecord: error %d (%s)\n",
761243ca327SMatthew Dillon 			errno, strerror(errno));
762243ca327SMatthew Dillon 		exit(1);
763243ca327SMatthew Dillon 	}
76417dd83bcSMatthew Dillon 	if (pad) {
76517dd83bcSMatthew Dillon 		bzero(zbuf, pad);
76617dd83bcSMatthew Dillon 		if (write(fdout, zbuf, pad) != pad) {
76717dd83bcSMatthew Dillon 			fprintf(stderr, "write_mrecord: error %d (%s)\n",
76817dd83bcSMatthew Dillon 				errno, strerror(errno));
76917dd83bcSMatthew Dillon 			exit(1);
77017dd83bcSMatthew Dillon 		}
77117dd83bcSMatthew Dillon 	}
772243ca327SMatthew Dillon }
773243ca327SMatthew Dillon 
774243ca327SMatthew Dillon /*
77534ebae70SMatthew Dillon  * Generate a mirroring header with the pfs information of the
77634ebae70SMatthew Dillon  * originating filesytem.
77734ebae70SMatthew Dillon  */
77834ebae70SMatthew Dillon static void
779d4e5b69bSMatthew Dillon generate_mrec_header(int fd, int fdout, int pfs_id,
78034ebae70SMatthew Dillon 		     hammer_tid_t *tid_begp, hammer_tid_t *tid_endp)
78134ebae70SMatthew Dillon {
78234ebae70SMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
78317dd83bcSMatthew Dillon 	union hammer_ioc_mrecord_any mrec_tmp;
78434ebae70SMatthew Dillon 
78534ebae70SMatthew Dillon 	bzero(&pfs, sizeof(pfs));
78617dd83bcSMatthew Dillon 	bzero(&mrec_tmp, sizeof(mrec_tmp));
787d4e5b69bSMatthew Dillon 	pfs.pfs_id = pfs_id;
78817dd83bcSMatthew Dillon 	pfs.ondisk = &mrec_tmp.pfs.pfsd;
78917dd83bcSMatthew Dillon 	pfs.bytes = sizeof(mrec_tmp.pfs.pfsd);
79034ebae70SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
791*9dc76cb1SMatthew Dillon 		fprintf(stderr, "Mirror-read: not a HAMMER fs/pseudofs!\n");
79234ebae70SMatthew Dillon 		exit(1);
79334ebae70SMatthew Dillon 	}
79434ebae70SMatthew Dillon 	if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
795*9dc76cb1SMatthew Dillon 		fprintf(stderr, "Mirror-read: HAMMER pfs version mismatch!\n");
79634ebae70SMatthew Dillon 		exit(1);
79734ebae70SMatthew Dillon 	}
79834ebae70SMatthew Dillon 
79934ebae70SMatthew Dillon 	/*
80034ebae70SMatthew Dillon 	 * sync_beg_tid - lowest TID on source after which a full history
80134ebae70SMatthew Dillon 	 *	 	  is available.
80234ebae70SMatthew Dillon 	 *
80334ebae70SMatthew Dillon 	 * sync_end_tid - highest fully synchronized TID from source.
80434ebae70SMatthew Dillon 	 */
80517dd83bcSMatthew Dillon 	if (tid_begp && *tid_begp < mrec_tmp.pfs.pfsd.sync_beg_tid)
80617dd83bcSMatthew Dillon 		*tid_begp = mrec_tmp.pfs.pfsd.sync_beg_tid;
807d4e5b69bSMatthew Dillon 	if (tid_endp)
80817dd83bcSMatthew Dillon 		*tid_endp = mrec_tmp.pfs.pfsd.sync_end_tid;
80917dd83bcSMatthew Dillon 	mrec_tmp.pfs.version = pfs.version;
810243ca327SMatthew Dillon 	write_mrecord(fdout, HAMMER_MREC_TYPE_PFSD,
81117dd83bcSMatthew Dillon 		      &mrec_tmp, sizeof(mrec_tmp.pfs));
81234ebae70SMatthew Dillon }
81334ebae70SMatthew Dillon 
81434ebae70SMatthew Dillon /*
81534ebae70SMatthew Dillon  * Validate the pfs information from the originating filesystem
81634ebae70SMatthew Dillon  * against the target filesystem.  shared_uuid must match.
81734ebae70SMatthew Dillon  */
81834ebae70SMatthew Dillon static void
819d4e5b69bSMatthew Dillon validate_mrec_header(int fd, int fdin, int is_target, int pfs_id,
82034ebae70SMatthew Dillon 		     hammer_tid_t *tid_begp, hammer_tid_t *tid_endp)
82134ebae70SMatthew Dillon {
82234ebae70SMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
82334ebae70SMatthew Dillon 	struct hammer_pseudofs_data pfsd;
82417dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
825243ca327SMatthew Dillon 	int error;
82634ebae70SMatthew Dillon 
82734ebae70SMatthew Dillon 	/*
82834ebae70SMatthew Dillon 	 * Get the PFSD info from the target filesystem.
82934ebae70SMatthew Dillon 	 */
83034ebae70SMatthew Dillon 	bzero(&pfs, sizeof(pfs));
83134ebae70SMatthew Dillon 	bzero(&pfsd, sizeof(pfsd));
832d4e5b69bSMatthew Dillon 	pfs.pfs_id = pfs_id;
83334ebae70SMatthew Dillon 	pfs.ondisk = &pfsd;
83434ebae70SMatthew Dillon 	pfs.bytes = sizeof(pfsd);
83534ebae70SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
83634ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: not a HAMMER fs/pseudofs!\n");
83734ebae70SMatthew Dillon 		exit(1);
83834ebae70SMatthew Dillon 	}
83934ebae70SMatthew Dillon 	if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
84034ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: HAMMER pfs version mismatch!\n");
84134ebae70SMatthew Dillon 		exit(1);
84234ebae70SMatthew Dillon 	}
84334ebae70SMatthew Dillon 
844243ca327SMatthew Dillon 	mrec = read_mrecord(fdin, &error, NULL);
845243ca327SMatthew Dillon 	if (mrec == NULL) {
846243ca327SMatthew Dillon 		if (error == 0)
847243ca327SMatthew Dillon 			fprintf(stderr, "validate_mrec_header: short read\n");
84834ebae70SMatthew Dillon 		exit(1);
84934ebae70SMatthew Dillon 	}
85017dd83bcSMatthew Dillon 	if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) {
851243ca327SMatthew Dillon 		fprintf(stderr, "validate_mrec_header: did not get expected "
852243ca327SMatthew Dillon 				"PFSD record type\n");
85334ebae70SMatthew Dillon 		exit(1);
85434ebae70SMatthew Dillon 	}
85517dd83bcSMatthew Dillon 	if (mrec->head.rec_size != sizeof(mrec->pfs)) {
856243ca327SMatthew Dillon 		fprintf(stderr, "validate_mrec_header: unexpected payload "
857243ca327SMatthew Dillon 				"size\n");
85834ebae70SMatthew Dillon 		exit(1);
85934ebae70SMatthew Dillon 	}
86017dd83bcSMatthew Dillon 	if (mrec->pfs.version != pfs.version) {
861243ca327SMatthew Dillon 		fprintf(stderr, "validate_mrec_header: Version mismatch\n");
86234ebae70SMatthew Dillon 		exit(1);
86334ebae70SMatthew Dillon 	}
86434ebae70SMatthew Dillon 
86534ebae70SMatthew Dillon 	/*
86634ebae70SMatthew Dillon 	 * Whew.  Ok, is the read PFS info compatible with the target?
86734ebae70SMatthew Dillon 	 */
86817dd83bcSMatthew Dillon 	if (bcmp(&mrec->pfs.pfsd.shared_uuid, &pfsd.shared_uuid,
86917dd83bcSMatthew Dillon 		 sizeof(pfsd.shared_uuid)) != 0) {
87017dd83bcSMatthew Dillon 		fprintf(stderr,
87117dd83bcSMatthew Dillon 			"mirror-write: source and target have "
87217dd83bcSMatthew Dillon 			"different shared_uuid's!\n");
87334ebae70SMatthew Dillon 		exit(1);
87434ebae70SMatthew Dillon 	}
875d4e5b69bSMatthew Dillon 	if (is_target &&
876d4e5b69bSMatthew Dillon 	    (pfsd.mirror_flags & HAMMER_PFSD_SLAVE) == 0) {
87734ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: target must be in slave mode\n");
87834ebae70SMatthew Dillon 		exit(1);
87934ebae70SMatthew Dillon 	}
880d4e5b69bSMatthew Dillon 	if (tid_begp)
88117dd83bcSMatthew Dillon 		*tid_begp = mrec->pfs.pfsd.sync_beg_tid;
882d4e5b69bSMatthew Dillon 	if (tid_endp)
88317dd83bcSMatthew Dillon 		*tid_endp = mrec->pfs.pfsd.sync_end_tid;
884243ca327SMatthew Dillon 	free(mrec);
88534ebae70SMatthew Dillon }
88634ebae70SMatthew Dillon 
88734ebae70SMatthew Dillon static void
888d4e5b69bSMatthew Dillon update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id)
88934ebae70SMatthew Dillon {
890243ca327SMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
891243ca327SMatthew Dillon 	struct hammer_pseudofs_data pfsd;
89234ebae70SMatthew Dillon 
893243ca327SMatthew Dillon 	bzero(&pfs, sizeof(pfs));
894243ca327SMatthew Dillon 	bzero(&pfsd, sizeof(pfsd));
895d4e5b69bSMatthew Dillon 	pfs.pfs_id = pfs_id;
896243ca327SMatthew Dillon 	pfs.ondisk = &pfsd;
897243ca327SMatthew Dillon 	pfs.bytes = sizeof(pfsd);
898243ca327SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
899243ca327SMatthew Dillon 		perror("update_pfs_snapshot (read)");
900243ca327SMatthew Dillon 		exit(1);
90134ebae70SMatthew Dillon 	}
9029c67b4d2SMatthew Dillon 	if (pfsd.sync_end_tid != snapshot_tid) {
903ddc8e722SMatthew Dillon 		pfsd.sync_end_tid = snapshot_tid;
904243ca327SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) != 0) {
905243ca327SMatthew Dillon 			perror("update_pfs_snapshot (rewrite)");
906243ca327SMatthew Dillon 			exit(1);
90734ebae70SMatthew Dillon 		}
908*9dc76cb1SMatthew Dillon 		fprintf(stderr,
909*9dc76cb1SMatthew Dillon 			"Mirror-write: Completed, updated snapshot "
910*9dc76cb1SMatthew Dillon 			"to %016llx\n",
911*9dc76cb1SMatthew Dillon 			snapshot_tid);
912243ca327SMatthew Dillon 	}
9139c67b4d2SMatthew Dillon }
914243ca327SMatthew Dillon 
91534ebae70SMatthew Dillon 
916a7fbbf91SMatthew Dillon static void
917a7fbbf91SMatthew Dillon mirror_usage(int code)
918a7fbbf91SMatthew Dillon {
919a7fbbf91SMatthew Dillon 	fprintf(stderr,
920a7fbbf91SMatthew Dillon 		"hammer mirror-read <filesystem>\n"
921a7fbbf91SMatthew Dillon 		"hammer mirror-write <filesystem>\n"
922243ca327SMatthew Dillon 		"hammer mirror-dump\n"
923a7fbbf91SMatthew Dillon 		"hammer mirror-copy [[user@]host:]fs [[user@]host:]fs\n"
924a7fbbf91SMatthew Dillon 	);
925a7fbbf91SMatthew Dillon 	exit(code);
926a7fbbf91SMatthew Dillon }
927