xref: /dflybsd-src/sbin/hammer/cmd_mirror.c (revision 9c67b4d2b6dba8bd1db3343a9fa5ced441a9c7f2)
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*9c67b4d2SMatthew Dillon  * $DragonFly: src/sbin/hammer/cmd_mirror.c,v 1.8 2008/07/12 02:48:46 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 
114243ca327SMatthew 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) {
117243ca327SMatthew Dillon 		fprintf(stderr, "mirror-read: Resuming at object %016llx\n",
118243ca327SMatthew Dillon 			mirror.key_beg.obj_id);
119243ca327SMatthew Dillon 	}
120243ca327SMatthew Dillon 
121243ca327SMatthew Dillon 	/*
122243ca327SMatthew Dillon 	 * Write out bulk records
123243ca327SMatthew Dillon 	 */
124a7fbbf91SMatthew Dillon 	mirror.ubuf = buf;
125a7fbbf91SMatthew Dillon 	mirror.size = SERIALBUF_SIZE;
126a7fbbf91SMatthew Dillon 
127a7fbbf91SMatthew Dillon 	do {
128a7fbbf91SMatthew Dillon 		mirror.count = 0;
129d4e5b69bSMatthew Dillon 		mirror.pfs_id = pfs.pfs_id;
130d4e5b69bSMatthew Dillon 		mirror.shared_uuid = pfs.ondisk->shared_uuid;
131a7fbbf91SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) {
132a7fbbf91SMatthew Dillon 			fprintf(stderr, "Mirror-read %s failed: %s\n",
133a7fbbf91SMatthew Dillon 				filesystem, strerror(errno));
134a7fbbf91SMatthew Dillon 			exit(1);
135a7fbbf91SMatthew Dillon 		}
136*9c67b4d2SMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) {
137*9c67b4d2SMatthew Dillon 			fprintf(stderr,
138*9c67b4d2SMatthew Dillon 				"Mirror-read %s fatal error %d\n",
139*9c67b4d2SMatthew Dillon 				filesystem, mirror.head.error);
140*9c67b4d2SMatthew Dillon 			exit(1);
141*9c67b4d2SMatthew Dillon 		}
142243ca327SMatthew Dillon 		if (mirror.count) {
143243ca327SMatthew Dillon 			n = write(1, mirror.ubuf, mirror.count);
144243ca327SMatthew Dillon 			if (n != mirror.count) {
145243ca327SMatthew Dillon 				fprintf(stderr, "Mirror-read %s failed: "
146243ca327SMatthew Dillon 						"short write\n",
147243ca327SMatthew Dillon 				filesystem);
148243ca327SMatthew Dillon 				exit(1);
149243ca327SMatthew Dillon 			}
150a7fbbf91SMatthew Dillon 		}
151a7fbbf91SMatthew Dillon 		mirror.key_beg = mirror.key_cur;
152243ca327SMatthew Dillon 		if (TimeoutOpt &&
153243ca327SMatthew Dillon 		    (unsigned)(time(NULL) - base_t) > (unsigned)TimeoutOpt) {
154243ca327SMatthew Dillon 			fprintf(stderr,
155243ca327SMatthew Dillon 				"Mirror-read %s interrupted by timer at"
156243ca327SMatthew Dillon 				" %016llx\n",
157243ca327SMatthew Dillon 				filesystem,
158243ca327SMatthew Dillon 				mirror.key_cur.obj_id);
159243ca327SMatthew Dillon 			interrupted = 1;
160243ca327SMatthew Dillon 			break;
161243ca327SMatthew Dillon 		}
162a7fbbf91SMatthew Dillon 	} while (mirror.count != 0);
163a7fbbf91SMatthew Dillon 
164243ca327SMatthew Dillon 	/*
165243ca327SMatthew Dillon 	 * Write out the termination sync record
166243ca327SMatthew Dillon 	 */
16717dd83bcSMatthew Dillon 	write_mrecord(1, HAMMER_MREC_TYPE_SYNC,
16817dd83bcSMatthew Dillon 		      &mrec_tmp, sizeof(mrec_tmp.sync));
16934ebae70SMatthew Dillon 
170243ca327SMatthew Dillon 	/*
171243ca327SMatthew Dillon 	 * If the -2 option was given (automatic when doing mirror-copy),
172243ca327SMatthew Dillon 	 * a two-way pipe is assumed and we expect a response mrec from
173243ca327SMatthew Dillon 	 * the target.
174243ca327SMatthew Dillon 	 */
175243ca327SMatthew Dillon 	if (TwoWayPipeOpt) {
176243ca327SMatthew Dillon 		mrec = read_mrecord(0, &error, NULL);
17717dd83bcSMatthew Dillon 		if (mrec == NULL ||
17817dd83bcSMatthew Dillon 		    mrec->head.type != HAMMER_MREC_TYPE_UPDATE ||
17917dd83bcSMatthew Dillon 		    mrec->head.rec_size != sizeof(mrec->update)) {
180243ca327SMatthew Dillon 			fprintf(stderr, "mirror_read: Did not get final "
181243ca327SMatthew Dillon 					"acknowledgement packet from target\n");
182243ca327SMatthew Dillon 			exit(1);
183243ca327SMatthew Dillon 		}
184243ca327SMatthew Dillon 		if (interrupted) {
185243ca327SMatthew Dillon 			if (CyclePath) {
186243ca327SMatthew Dillon 				hammer_set_cycle(&mirror.key_cur, mirror.tid_beg);
187243ca327SMatthew Dillon 				fprintf(stderr, "Cyclefile %s updated for continuation\n", CyclePath);
188243ca327SMatthew Dillon 			}
189243ca327SMatthew Dillon 		} else {
19017dd83bcSMatthew Dillon 			sync_tid = mrec->update.tid;
191243ca327SMatthew Dillon 			if (CyclePath) {
192243ca327SMatthew Dillon 				hammer_key_beg_init(&mirror.key_beg);
193243ca327SMatthew Dillon 				hammer_set_cycle(&mirror.key_beg, sync_tid);
194243ca327SMatthew Dillon 				fprintf(stderr, "Cyclefile %s updated to 0x%016llx\n",
195243ca327SMatthew Dillon 					CyclePath, sync_tid);
196243ca327SMatthew Dillon 			} else {
197243ca327SMatthew Dillon 				fprintf(stderr, "Source can update synctid "
198243ca327SMatthew Dillon 						"to 0x%016llx\n",
199243ca327SMatthew Dillon 					sync_tid);
200243ca327SMatthew Dillon 			}
201243ca327SMatthew Dillon 		}
202243ca327SMatthew Dillon 	} else if (CyclePath) {
203243ca327SMatthew Dillon 		/* NOTE! mirror.tid_beg cannot be updated */
204243ca327SMatthew Dillon 		fprintf(stderr, "Warning: cycle file (-c option) cannot be "
205243ca327SMatthew Dillon 				"fully updated unless you use mirror-copy\n");
206243ca327SMatthew Dillon 		hammer_set_cycle(&mirror.key_beg, mirror.tid_beg);
207243ca327SMatthew Dillon 	}
208a7fbbf91SMatthew Dillon 	fprintf(stderr, "Mirror-read %s succeeded\n", filesystem);
209a7fbbf91SMatthew Dillon }
210a7fbbf91SMatthew Dillon 
21117dd83bcSMatthew Dillon /*
21217dd83bcSMatthew Dillon  * Pipe the mirroring data stream on stdin to the HAMMER VFS, adding
21317dd83bcSMatthew Dillon  * some additional packet types to negotiate TID ranges and to verify
21417dd83bcSMatthew Dillon  * completion.  The HAMMER VFS does most of the work.
21517dd83bcSMatthew Dillon  *
21617dd83bcSMatthew Dillon  * It is important to note that the mirror.key_{beg,end} range must
21717dd83bcSMatthew Dillon  * match the ranged used by the original.  For now both sides use
21817dd83bcSMatthew Dillon  * range the entire key space.
21917dd83bcSMatthew Dillon  *
22017dd83bcSMatthew Dillon  * It is even more important that the records in the stream conform
22117dd83bcSMatthew Dillon  * to the TID range also supplied in the stream.  The HAMMER VFS will
22217dd83bcSMatthew Dillon  * use the REC, PASS, and SKIP record types to track the portions of
22317dd83bcSMatthew Dillon  * the B-Tree being scanned in order to be able to proactively delete
22417dd83bcSMatthew Dillon  * records on the target within those active areas that are not mentioned
22517dd83bcSMatthew Dillon  * by the source.
22617dd83bcSMatthew Dillon  *
22717dd83bcSMatthew Dillon  * The mirror.key_cur field is used by the VFS to do this tracking.  It
22817dd83bcSMatthew Dillon  * must be initialized to key_beg but then is persistently updated by
22917dd83bcSMatthew Dillon  * the HAMMER VFS on each successive ioctl() call.  If you blow up this
23017dd83bcSMatthew Dillon  * field you will blow up the mirror target, possibly to the point of
23117dd83bcSMatthew Dillon  * deleting everything.  As a safety measure the HAMMER VFS simply marks
23217dd83bcSMatthew Dillon  * the records that the source has destroyed as deleted on the target,
23317dd83bcSMatthew Dillon  * and normal pruning operations will deal with their final disposition
23417dd83bcSMatthew Dillon  * at some later time.
23517dd83bcSMatthew Dillon  */
236a7fbbf91SMatthew Dillon void
237a7fbbf91SMatthew Dillon hammer_cmd_mirror_write(char **av, int ac)
238a7fbbf91SMatthew Dillon {
239a7fbbf91SMatthew Dillon 	struct hammer_ioc_mirror_rw mirror;
240a7fbbf91SMatthew Dillon 	const char *filesystem;
241a7fbbf91SMatthew Dillon 	char *buf = malloc(SERIALBUF_SIZE);
242d4e5b69bSMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
24317dd83bcSMatthew Dillon 	struct hammer_ioc_mrecord_head pickup;
244243ca327SMatthew Dillon 	struct hammer_ioc_synctid synctid;
24517dd83bcSMatthew Dillon 	union hammer_ioc_mrecord_any mrec_tmp;
24617dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
247243ca327SMatthew Dillon 	int error;
248243ca327SMatthew Dillon 	int fd;
249a7fbbf91SMatthew Dillon 
250a7fbbf91SMatthew Dillon 	if (ac > 2)
251a7fbbf91SMatthew Dillon 		mirror_usage(1);
252a7fbbf91SMatthew Dillon 	filesystem = av[0];
253a7fbbf91SMatthew Dillon 
254a7fbbf91SMatthew Dillon 	bzero(&mirror, sizeof(mirror));
255a7fbbf91SMatthew Dillon 	hammer_key_beg_init(&mirror.key_beg);
256a7fbbf91SMatthew Dillon 	hammer_key_end_init(&mirror.key_end);
25717dd83bcSMatthew Dillon 	mirror.key_end = mirror.key_beg;
258a7fbbf91SMatthew Dillon 
259d4e5b69bSMatthew Dillon 	fd = getpfs(&pfs, filesystem);
260a7fbbf91SMatthew Dillon 
261243ca327SMatthew Dillon 	/*
262d4e5b69bSMatthew Dillon 	 * In two-way mode the target writes out a PFS packet first.
263d4e5b69bSMatthew Dillon 	 * The source uses our tid_end as its tid_beg by default,
264d4e5b69bSMatthew Dillon 	 * picking up where it left off.
265243ca327SMatthew Dillon 	 */
266d4e5b69bSMatthew Dillon 	mirror.tid_beg = 0;
267d4e5b69bSMatthew Dillon 	if (TwoWayPipeOpt) {
268d4e5b69bSMatthew Dillon 		generate_mrec_header(fd, 1, pfs.pfs_id,
269d4e5b69bSMatthew Dillon 				     &mirror.tid_beg, &mirror.tid_end);
270d4e5b69bSMatthew Dillon 	}
271d4e5b69bSMatthew Dillon 
272d4e5b69bSMatthew Dillon 	/*
27317dd83bcSMatthew Dillon 	 * Read and process the PFS header.  The source informs us of
27417dd83bcSMatthew Dillon 	 * the TID range the stream represents.
275d4e5b69bSMatthew Dillon 	 */
276d4e5b69bSMatthew Dillon 	validate_mrec_header(fd, 0, 1, pfs.pfs_id,
277d4e5b69bSMatthew Dillon 			     &mirror.tid_beg, &mirror.tid_end);
27834ebae70SMatthew Dillon 
279a7fbbf91SMatthew Dillon 	mirror.ubuf = buf;
280a7fbbf91SMatthew Dillon 	mirror.size = SERIALBUF_SIZE;
281a7fbbf91SMatthew Dillon 
282a7fbbf91SMatthew Dillon 	pickup.signature = 0;
283243ca327SMatthew Dillon 	pickup.type = 0;
284a7fbbf91SMatthew Dillon 
285243ca327SMatthew Dillon 	/*
28617dd83bcSMatthew Dillon 	 * Read and process bulk records (REC, PASS, and SKIP types).
28717dd83bcSMatthew Dillon 	 *
28817dd83bcSMatthew Dillon 	 * On your life, do NOT mess with mirror.key_cur or your mirror
28917dd83bcSMatthew Dillon 	 * target may become history.
290243ca327SMatthew Dillon 	 */
291a7fbbf91SMatthew Dillon 	for (;;) {
292a7fbbf91SMatthew Dillon 		mirror.count = 0;
293d4e5b69bSMatthew Dillon 		mirror.pfs_id = pfs.pfs_id;
294d4e5b69bSMatthew Dillon 		mirror.shared_uuid = pfs.ondisk->shared_uuid;
295a7fbbf91SMatthew Dillon 		mirror.size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup);
296a7fbbf91SMatthew Dillon 		if (mirror.size <= 0)
297a7fbbf91SMatthew Dillon 			break;
298a7fbbf91SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_MIRROR_WRITE, &mirror) < 0) {
299a7fbbf91SMatthew Dillon 			fprintf(stderr, "Mirror-write %s failed: %s\n",
300a7fbbf91SMatthew Dillon 				filesystem, strerror(errno));
301a7fbbf91SMatthew Dillon 			exit(1);
302a7fbbf91SMatthew Dillon 		}
30317dd83bcSMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) {
30417dd83bcSMatthew Dillon 			fprintf(stderr,
30517dd83bcSMatthew Dillon 				"Mirror-write %s fatal error %d\n",
30617dd83bcSMatthew Dillon 				filesystem, mirror.head.error);
30717dd83bcSMatthew Dillon 			exit(1);
30817dd83bcSMatthew Dillon 		}
309243ca327SMatthew Dillon #if 0
310a7fbbf91SMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_INTR) {
311a7fbbf91SMatthew Dillon 			fprintf(stderr,
312a7fbbf91SMatthew Dillon 				"Mirror-write %s interrupted by timer at"
313243ca327SMatthew Dillon 				" %016llx\n",
314a7fbbf91SMatthew Dillon 				filesystem,
315243ca327SMatthew Dillon 				mirror.key_cur.obj_id);
316a7fbbf91SMatthew Dillon 			exit(0);
317a7fbbf91SMatthew Dillon 		}
318243ca327SMatthew Dillon #endif
319a7fbbf91SMatthew Dillon 	}
320243ca327SMatthew Dillon 
321243ca327SMatthew Dillon 	/*
322243ca327SMatthew Dillon 	 * Read and process the termination sync record.
323243ca327SMatthew Dillon 	 */
324243ca327SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
32517dd83bcSMatthew Dillon 	if (mrec == NULL ||
32617dd83bcSMatthew Dillon 	    mrec->head.type != HAMMER_MREC_TYPE_SYNC ||
32717dd83bcSMatthew Dillon 	    mrec->head.rec_size != sizeof(mrec->sync)) {
328243ca327SMatthew Dillon 		fprintf(stderr, "Mirror-write %s: Did not get termination "
32917dd83bcSMatthew Dillon 				"sync record, or rec_size is wrong rt=%d\n",
33017dd83bcSMatthew Dillon 				filesystem, mrec->head.type);
331243ca327SMatthew Dillon 	}
33217dd83bcSMatthew Dillon 	free(mrec);
33317dd83bcSMatthew Dillon 	mrec = NULL;
334243ca327SMatthew Dillon 
335243ca327SMatthew Dillon 	/*
336243ca327SMatthew Dillon 	 * Update the PFS info on the target so the user has visibility
337243ca327SMatthew Dillon 	 * into the new snapshot.
338243ca327SMatthew Dillon 	 */
339d4e5b69bSMatthew Dillon 	update_pfs_snapshot(fd, mirror.tid_end, pfs.pfs_id);
340243ca327SMatthew Dillon 
341243ca327SMatthew Dillon 	/*
342243ca327SMatthew Dillon 	 * Sync the target filesystem
343243ca327SMatthew Dillon 	 */
344243ca327SMatthew Dillon 	bzero(&synctid, sizeof(synctid));
345243ca327SMatthew Dillon 	synctid.op = HAMMER_SYNCTID_SYNC2;
346243ca327SMatthew Dillon 	ioctl(fd, HAMMERIOC_SYNCTID, &synctid);
347243ca327SMatthew Dillon 
348243ca327SMatthew Dillon 	fprintf(stderr, "Mirror-write %s: succeeded\n", filesystem);
349243ca327SMatthew Dillon 
350243ca327SMatthew Dillon 	/*
351243ca327SMatthew Dillon 	 * Report back to the originator.
352243ca327SMatthew Dillon 	 */
353243ca327SMatthew Dillon 	if (TwoWayPipeOpt) {
35417dd83bcSMatthew Dillon 		mrec_tmp.update.tid = mirror.tid_end;
355243ca327SMatthew Dillon 		write_mrecord(1, HAMMER_MREC_TYPE_UPDATE,
35617dd83bcSMatthew Dillon 			      &mrec_tmp, sizeof(mrec_tmp.update));
357243ca327SMatthew Dillon 	} else {
358243ca327SMatthew Dillon 		printf("Source can update synctid to 0x%016llx\n",
359243ca327SMatthew Dillon 		       mirror.tid_end);
360243ca327SMatthew Dillon 	}
361243ca327SMatthew Dillon }
362243ca327SMatthew Dillon 
363243ca327SMatthew Dillon void
364243ca327SMatthew Dillon hammer_cmd_mirror_dump(void)
365243ca327SMatthew Dillon {
366243ca327SMatthew Dillon 	char *buf = malloc(SERIALBUF_SIZE);
36717dd83bcSMatthew Dillon 	struct hammer_ioc_mrecord_head pickup;
36817dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
369243ca327SMatthew Dillon 	int error;
370243ca327SMatthew Dillon 	int size;
37117dd83bcSMatthew Dillon 	int offset;
37217dd83bcSMatthew Dillon 	int bytes;
373243ca327SMatthew Dillon 
374243ca327SMatthew Dillon 	/*
375243ca327SMatthew Dillon 	 * Read and process the PFS header
376243ca327SMatthew Dillon 	 */
377243ca327SMatthew Dillon 	pickup.signature = 0;
378243ca327SMatthew Dillon 	pickup.type = 0;
379243ca327SMatthew Dillon 
380243ca327SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
381243ca327SMatthew Dillon 
382243ca327SMatthew Dillon 	/*
383243ca327SMatthew Dillon 	 * Read and process bulk records
384243ca327SMatthew Dillon 	 */
385243ca327SMatthew Dillon 	for (;;) {
386243ca327SMatthew Dillon 		size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup);
387243ca327SMatthew Dillon 		if (size <= 0)
388243ca327SMatthew Dillon 			break;
38917dd83bcSMatthew Dillon 		offset = 0;
39017dd83bcSMatthew Dillon 		while (offset < size) {
39117dd83bcSMatthew Dillon 			mrec = (void *)((char *)buf + offset);
39217dd83bcSMatthew Dillon 			bytes = HAMMER_HEAD_DOALIGN(mrec->head.rec_size);
39317dd83bcSMatthew Dillon 			if (offset + bytes > size) {
39417dd83bcSMatthew Dillon 				fprintf(stderr, "Misaligned record\n");
39517dd83bcSMatthew Dillon 				exit(1);
39617dd83bcSMatthew Dillon 			}
39717dd83bcSMatthew Dillon 
39817dd83bcSMatthew Dillon 			switch(mrec->head.type) {
39917dd83bcSMatthew Dillon 			case HAMMER_MREC_TYPE_REC:
400243ca327SMatthew Dillon 				printf("Record obj=%016llx key=%016llx "
401243ca327SMatthew Dillon 				       "rt=%02x ot=%02x\n",
40217dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_id,
40317dd83bcSMatthew Dillon 					mrec->rec.leaf.base.key,
40417dd83bcSMatthew Dillon 					mrec->rec.leaf.base.rec_type,
40517dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_type);
406243ca327SMatthew Dillon 				printf("       tids %016llx:%016llx data=%d\n",
40717dd83bcSMatthew Dillon 					mrec->rec.leaf.base.create_tid,
40817dd83bcSMatthew Dillon 					mrec->rec.leaf.base.delete_tid,
40917dd83bcSMatthew Dillon 					mrec->rec.leaf.data_len);
41017dd83bcSMatthew Dillon 				break;
41117dd83bcSMatthew Dillon 			case HAMMER_MREC_TYPE_PASS:
41217dd83bcSMatthew Dillon 				printf("Pass   obj=%016llx key=%016llx "
41317dd83bcSMatthew Dillon 				       "rt=%02x ot=%02x\n",
41417dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_id,
41517dd83bcSMatthew Dillon 					mrec->rec.leaf.base.key,
41617dd83bcSMatthew Dillon 					mrec->rec.leaf.base.rec_type,
41717dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_type);
41817dd83bcSMatthew Dillon 				printf("       tids %016llx:%016llx data=%d\n",
41917dd83bcSMatthew Dillon 					mrec->rec.leaf.base.create_tid,
42017dd83bcSMatthew Dillon 					mrec->rec.leaf.base.delete_tid,
42117dd83bcSMatthew Dillon 					mrec->rec.leaf.data_len);
42217dd83bcSMatthew Dillon 				break;
42317dd83bcSMatthew Dillon 			case HAMMER_MREC_TYPE_SKIP:
42417dd83bcSMatthew Dillon 				printf("Skip   obj=%016llx key=%016llx rt=%02x to\n"
42517dd83bcSMatthew Dillon 				       "       obj=%016llx key=%016llx rt=%02x\n",
42617dd83bcSMatthew Dillon 				       mrec->skip.skip_beg.obj_id,
42717dd83bcSMatthew Dillon 				       mrec->skip.skip_beg.key,
42817dd83bcSMatthew Dillon 				       mrec->skip.skip_beg.rec_type,
42917dd83bcSMatthew Dillon 				       mrec->skip.skip_end.obj_id,
43017dd83bcSMatthew Dillon 				       mrec->skip.skip_end.key,
43117dd83bcSMatthew Dillon 				       mrec->skip.skip_end.rec_type);
43217dd83bcSMatthew Dillon 			default:
43317dd83bcSMatthew Dillon 				break;
43417dd83bcSMatthew Dillon 			}
43517dd83bcSMatthew Dillon 			offset += bytes;
436243ca327SMatthew Dillon 		}
437243ca327SMatthew Dillon 	}
438243ca327SMatthew Dillon 
439243ca327SMatthew Dillon 	/*
440243ca327SMatthew Dillon 	 * Read and process the termination sync record.
441243ca327SMatthew Dillon 	 */
442243ca327SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
44317dd83bcSMatthew Dillon 	if (mrec == NULL || mrec->head.type != HAMMER_MREC_TYPE_SYNC) {
444243ca327SMatthew Dillon 		fprintf(stderr, "Mirror-dump: Did not get termination "
445243ca327SMatthew Dillon 				"sync record\n");
446243ca327SMatthew Dillon 	}
447a7fbbf91SMatthew Dillon }
448a7fbbf91SMatthew Dillon 
449a7fbbf91SMatthew Dillon void
450a7fbbf91SMatthew Dillon hammer_cmd_mirror_copy(char **av, int ac)
451a7fbbf91SMatthew Dillon {
45234ebae70SMatthew Dillon 	pid_t pid1;
45334ebae70SMatthew Dillon 	pid_t pid2;
45434ebae70SMatthew Dillon 	int fds[2];
455243ca327SMatthew Dillon 	const char *xav[16];
456243ca327SMatthew Dillon 	char tbuf[16];
45734ebae70SMatthew Dillon 	char *ptr;
458243ca327SMatthew Dillon 	int xac;
45934ebae70SMatthew Dillon 
46034ebae70SMatthew Dillon 	if (ac != 2)
46134ebae70SMatthew Dillon 		mirror_usage(1);
46234ebae70SMatthew Dillon 
46334ebae70SMatthew Dillon 	if (pipe(fds) < 0) {
46434ebae70SMatthew Dillon 		perror("pipe");
46534ebae70SMatthew Dillon 		exit(1);
46634ebae70SMatthew Dillon 	}
46734ebae70SMatthew Dillon 
468243ca327SMatthew Dillon 	TwoWayPipeOpt = 1;
469243ca327SMatthew Dillon 
47034ebae70SMatthew Dillon 	/*
47134ebae70SMatthew Dillon 	 * Source
47234ebae70SMatthew Dillon 	 */
47334ebae70SMatthew Dillon 	if ((pid1 = fork()) == 0) {
47434ebae70SMatthew Dillon 		dup2(fds[0], 0);
47534ebae70SMatthew Dillon 		dup2(fds[0], 1);
47634ebae70SMatthew Dillon 		close(fds[0]);
47734ebae70SMatthew Dillon 		close(fds[1]);
47834ebae70SMatthew Dillon 		if ((ptr = strchr(av[0], ':')) != NULL) {
47934ebae70SMatthew Dillon 			*ptr++ = 0;
480243ca327SMatthew Dillon 			xac = 0;
481243ca327SMatthew Dillon 			xav[xac++] = "ssh";
482243ca327SMatthew Dillon 			xav[xac++] = av[0];
483243ca327SMatthew Dillon 			xav[xac++] = "hammer";
484243ca327SMatthew Dillon 			if (VerboseOpt)
485243ca327SMatthew Dillon 				xav[xac++] = "-v";
486243ca327SMatthew Dillon 			xav[xac++] = "-2";
487243ca327SMatthew Dillon 			if (TimeoutOpt) {
488243ca327SMatthew Dillon 				snprintf(tbuf, sizeof(tbuf), "%d", TimeoutOpt);
489243ca327SMatthew Dillon 				xav[xac++] = "-t";
490243ca327SMatthew Dillon 				xav[xac++] = tbuf;
491243ca327SMatthew Dillon 			}
492243ca327SMatthew Dillon 			xav[xac++] = "mirror-read";
493243ca327SMatthew Dillon 			xav[xac++] = ptr;
494243ca327SMatthew Dillon 			xav[xac++] = NULL;
495243ca327SMatthew Dillon 			execv("/usr/bin/ssh", (void *)xav);
49634ebae70SMatthew Dillon 		} else {
49734ebae70SMatthew Dillon 			hammer_cmd_mirror_read(av, 1);
498243ca327SMatthew Dillon 			fflush(stdout);
499243ca327SMatthew Dillon 			fflush(stderr);
50034ebae70SMatthew Dillon 		}
50153d93cc7SMatthew Dillon 		_exit(1);
50234ebae70SMatthew Dillon 	}
50334ebae70SMatthew Dillon 
50434ebae70SMatthew Dillon 	/*
50534ebae70SMatthew Dillon 	 * Target
50634ebae70SMatthew Dillon 	 */
50734ebae70SMatthew Dillon 	if ((pid2 = fork()) == 0) {
50834ebae70SMatthew Dillon 		dup2(fds[1], 0);
50934ebae70SMatthew Dillon 		dup2(fds[1], 1);
51034ebae70SMatthew Dillon 		close(fds[0]);
51134ebae70SMatthew Dillon 		close(fds[1]);
51234ebae70SMatthew Dillon 		if ((ptr = strchr(av[1], ':')) != NULL) {
51334ebae70SMatthew Dillon 			*ptr++ = 0;
514243ca327SMatthew Dillon 			xac = 0;
515243ca327SMatthew Dillon 			xav[xac++] = "ssh";
516243ca327SMatthew Dillon 			xav[xac++] = av[1];
517243ca327SMatthew Dillon 			xav[xac++] = "hammer";
518243ca327SMatthew Dillon 			if (VerboseOpt)
519243ca327SMatthew Dillon 				xav[xac++] = "-v";
520243ca327SMatthew Dillon 			xav[xac++] = "-2";
521243ca327SMatthew Dillon 			xav[xac++] = "mirror-write";
522243ca327SMatthew Dillon 			xav[xac++] = ptr;
523243ca327SMatthew Dillon 			xav[xac++] = NULL;
524243ca327SMatthew Dillon 			execv("/usr/bin/ssh", (void *)xav);
52534ebae70SMatthew Dillon 		} else {
52634ebae70SMatthew Dillon 			hammer_cmd_mirror_write(av + 1, 1);
527243ca327SMatthew Dillon 			fflush(stdout);
528243ca327SMatthew Dillon 			fflush(stderr);
52934ebae70SMatthew Dillon 		}
53053d93cc7SMatthew Dillon 		_exit(1);
53134ebae70SMatthew Dillon 	}
53234ebae70SMatthew Dillon 	close(fds[0]);
53334ebae70SMatthew Dillon 	close(fds[1]);
53434ebae70SMatthew Dillon 
53534ebae70SMatthew Dillon 	while (waitpid(pid1, NULL, 0) <= 0)
53634ebae70SMatthew Dillon 		;
53734ebae70SMatthew Dillon 	while (waitpid(pid2, NULL, 0) <= 0)
53834ebae70SMatthew Dillon 		;
539a7fbbf91SMatthew Dillon }
540a7fbbf91SMatthew Dillon 
541243ca327SMatthew Dillon /*
542243ca327SMatthew Dillon  * Read and return multiple mrecords
543243ca327SMatthew Dillon  */
544a7fbbf91SMatthew Dillon static int
54517dd83bcSMatthew Dillon read_mrecords(int fd, char *buf, u_int size, hammer_ioc_mrecord_head_t pickup)
546a7fbbf91SMatthew Dillon {
54717dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
548a7fbbf91SMatthew Dillon 	u_int count;
549a7fbbf91SMatthew Dillon 	size_t n;
550a7fbbf91SMatthew Dillon 	size_t i;
55117dd83bcSMatthew Dillon 	size_t bytes;
552a7fbbf91SMatthew Dillon 
553a7fbbf91SMatthew Dillon 	count = 0;
554a7fbbf91SMatthew Dillon 	while (size - count >= HAMMER_MREC_HEADSIZE) {
555a7fbbf91SMatthew Dillon 		/*
556a7fbbf91SMatthew Dillon 		 * Cached the record header in case we run out of buffer
557a7fbbf91SMatthew Dillon 		 * space.
558a7fbbf91SMatthew Dillon 		 */
55917dd83bcSMatthew Dillon 		fflush(stdout);
560a7fbbf91SMatthew Dillon 		if (pickup->signature == 0) {
561a7fbbf91SMatthew Dillon 			for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) {
562a7fbbf91SMatthew Dillon 				i = read(fd, (char *)pickup + n,
563a7fbbf91SMatthew Dillon 					 HAMMER_MREC_HEADSIZE - n);
564a7fbbf91SMatthew Dillon 				if (i <= 0)
565a7fbbf91SMatthew Dillon 					break;
566a7fbbf91SMatthew Dillon 			}
567a7fbbf91SMatthew Dillon 			if (n == 0)
568a7fbbf91SMatthew Dillon 				break;
569a7fbbf91SMatthew Dillon 			if (n != HAMMER_MREC_HEADSIZE) {
570a7fbbf91SMatthew Dillon 				fprintf(stderr, "read_mrecords: short read on pipe\n");
571a7fbbf91SMatthew Dillon 				exit(1);
572a7fbbf91SMatthew Dillon 			}
573a7fbbf91SMatthew Dillon 
574a7fbbf91SMatthew Dillon 			if (pickup->signature != HAMMER_IOC_MIRROR_SIGNATURE) {
575a7fbbf91SMatthew Dillon 				fprintf(stderr, "read_mrecords: malformed record on pipe, bad signature\n");
576a7fbbf91SMatthew Dillon 				exit(1);
577a7fbbf91SMatthew Dillon 			}
578a7fbbf91SMatthew Dillon 		}
579a7fbbf91SMatthew Dillon 		if (pickup->rec_size < HAMMER_MREC_HEADSIZE ||
58017dd83bcSMatthew Dillon 		    pickup->rec_size > sizeof(*mrec) + HAMMER_XBUFSIZE) {
581a7fbbf91SMatthew Dillon 			fprintf(stderr, "read_mrecords: malformed record on pipe, illegal rec_size\n");
582a7fbbf91SMatthew Dillon 			exit(1);
583a7fbbf91SMatthew Dillon 		}
584a7fbbf91SMatthew Dillon 
585a7fbbf91SMatthew Dillon 		/*
586a7fbbf91SMatthew Dillon 		 * Stop if we have insufficient space for the record and data.
587a7fbbf91SMatthew Dillon 		 */
58817dd83bcSMatthew Dillon 		bytes = HAMMER_HEAD_DOALIGN(pickup->rec_size);
58917dd83bcSMatthew Dillon 		if (size - count < bytes)
590a7fbbf91SMatthew Dillon 			break;
591a7fbbf91SMatthew Dillon 
592a7fbbf91SMatthew Dillon 		/*
59317dd83bcSMatthew Dillon 		 * Stop if the record type is not a REC or a SKIP (the only
59417dd83bcSMatthew Dillon 		 * two types the ioctl supports.  Other types are used only
59517dd83bcSMatthew Dillon 		 * by the userland protocol).
596243ca327SMatthew Dillon 		 */
59717dd83bcSMatthew Dillon 		if (pickup->type != HAMMER_MREC_TYPE_REC &&
59817dd83bcSMatthew Dillon 		    pickup->type != HAMMER_MREC_TYPE_SKIP &&
59917dd83bcSMatthew Dillon 		    pickup->type != HAMMER_MREC_TYPE_PASS) {
600243ca327SMatthew Dillon 			break;
60117dd83bcSMatthew Dillon 		}
602243ca327SMatthew Dillon 
603243ca327SMatthew Dillon 		/*
604a7fbbf91SMatthew Dillon 		 * Read the remainder and clear the pickup signature.
605a7fbbf91SMatthew Dillon 		 */
60617dd83bcSMatthew Dillon 		for (n = HAMMER_MREC_HEADSIZE; n < bytes; n += i) {
60717dd83bcSMatthew Dillon 			i = read(fd, buf + count + n, bytes - n);
608a7fbbf91SMatthew Dillon 			if (i <= 0)
609a7fbbf91SMatthew Dillon 				break;
610a7fbbf91SMatthew Dillon 		}
61117dd83bcSMatthew Dillon 		if (n != bytes) {
612a7fbbf91SMatthew Dillon 			fprintf(stderr, "read_mrecords: short read on pipe\n");
613a7fbbf91SMatthew Dillon 			exit(1);
614a7fbbf91SMatthew Dillon 		}
61517dd83bcSMatthew Dillon 
61617dd83bcSMatthew Dillon 		bcopy(pickup, buf + count, HAMMER_MREC_HEADSIZE);
61717dd83bcSMatthew Dillon 		pickup->signature = 0;
61817dd83bcSMatthew Dillon 		pickup->type = 0;
61917dd83bcSMatthew Dillon 		mrec = (void *)(buf + count);
62017dd83bcSMatthew Dillon 
62117dd83bcSMatthew Dillon 		/*
62217dd83bcSMatthew Dillon 		 * Validate the completed record
62317dd83bcSMatthew Dillon 		 */
62417dd83bcSMatthew Dillon 		if (mrec->head.rec_crc !=
62517dd83bcSMatthew Dillon 		    crc32((char *)mrec + HAMMER_MREC_CRCOFF,
62617dd83bcSMatthew Dillon 			  mrec->head.rec_size - HAMMER_MREC_CRCOFF)) {
62717dd83bcSMatthew Dillon 			fprintf(stderr, "read_mrecords: malformed record "
62817dd83bcSMatthew Dillon 					"on pipe, bad crc\n");
62917dd83bcSMatthew Dillon 			exit(1);
630a7fbbf91SMatthew Dillon 		}
631a7fbbf91SMatthew Dillon 
63217dd83bcSMatthew Dillon 		/*
63317dd83bcSMatthew Dillon 		 * If its a B-Tree record validate the data crc
63417dd83bcSMatthew Dillon 		 */
63517dd83bcSMatthew Dillon 		if (mrec->head.type == HAMMER_MREC_TYPE_REC) {
63617dd83bcSMatthew Dillon 			if (mrec->head.rec_size <
63717dd83bcSMatthew Dillon 			    sizeof(mrec->rec) + mrec->rec.leaf.data_len) {
63817dd83bcSMatthew Dillon 				fprintf(stderr,
63917dd83bcSMatthew Dillon 					"read_mrecords: malformed record on "
64017dd83bcSMatthew Dillon 					"pipe, illegal element data_len\n");
64117dd83bcSMatthew Dillon 				exit(1);
64217dd83bcSMatthew Dillon 			}
64317dd83bcSMatthew Dillon 			if (mrec->rec.leaf.data_len &&
64417dd83bcSMatthew Dillon 			    mrec->rec.leaf.data_offset &&
64517dd83bcSMatthew Dillon 			    hammer_crc_test_leaf(&mrec->rec + 1, &mrec->rec.leaf) == 0) {
64617dd83bcSMatthew Dillon 				fprintf(stderr,
64717dd83bcSMatthew Dillon 					"read_mrecords: data_crc did not "
64817dd83bcSMatthew Dillon 					"match data! obj=%016llx key=%016llx\n",
64917dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_id,
65017dd83bcSMatthew Dillon 					mrec->rec.leaf.base.key);
65117dd83bcSMatthew Dillon 				fprintf(stderr,
65217dd83bcSMatthew Dillon 					"continuing, but there are problems\n");
65317dd83bcSMatthew Dillon 			}
65417dd83bcSMatthew Dillon 		}
65517dd83bcSMatthew Dillon 		count += bytes;
656a7fbbf91SMatthew Dillon 	}
657a7fbbf91SMatthew Dillon 	return(count);
658a7fbbf91SMatthew Dillon }
659a7fbbf91SMatthew Dillon 
66034ebae70SMatthew Dillon /*
66117dd83bcSMatthew Dillon  * Read and return a single mrecord.
662243ca327SMatthew Dillon  */
663243ca327SMatthew Dillon static
66417dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t
66517dd83bcSMatthew Dillon read_mrecord(int fdin, int *errorp, hammer_ioc_mrecord_head_t pickup)
666243ca327SMatthew Dillon {
66717dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
66817dd83bcSMatthew Dillon 	struct hammer_ioc_mrecord_head mrechd;
669243ca327SMatthew Dillon 	size_t bytes;
670243ca327SMatthew Dillon 	size_t n;
671243ca327SMatthew Dillon 	size_t i;
672243ca327SMatthew Dillon 
673243ca327SMatthew Dillon 	if (pickup && pickup->type != 0) {
674243ca327SMatthew Dillon 		mrechd = *pickup;
675243ca327SMatthew Dillon 		pickup->signature = 0;
676243ca327SMatthew Dillon 		pickup->type = 0;
677243ca327SMatthew Dillon 		n = HAMMER_MREC_HEADSIZE;
678243ca327SMatthew Dillon 	} else {
679243ca327SMatthew Dillon 		/*
680243ca327SMatthew Dillon 		 * Read in the PFSD header from the sender.
681243ca327SMatthew Dillon 		 */
682243ca327SMatthew Dillon 		for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) {
683243ca327SMatthew Dillon 			i = read(fdin, (char *)&mrechd + n, HAMMER_MREC_HEADSIZE - n);
684243ca327SMatthew Dillon 			if (i <= 0)
685243ca327SMatthew Dillon 				break;
686243ca327SMatthew Dillon 		}
687243ca327SMatthew Dillon 		if (n == 0) {
688243ca327SMatthew Dillon 			*errorp = 0;	/* EOF */
689243ca327SMatthew Dillon 			return(NULL);
690243ca327SMatthew Dillon 		}
691243ca327SMatthew Dillon 		if (n != HAMMER_MREC_HEADSIZE) {
692243ca327SMatthew Dillon 			fprintf(stderr, "short read of mrecord header\n");
693243ca327SMatthew Dillon 			*errorp = EPIPE;
694243ca327SMatthew Dillon 			return(NULL);
695243ca327SMatthew Dillon 		}
696243ca327SMatthew Dillon 	}
697243ca327SMatthew Dillon 	if (mrechd.signature != HAMMER_IOC_MIRROR_SIGNATURE) {
698243ca327SMatthew Dillon 		fprintf(stderr, "read_mrecord: bad signature\n");
699243ca327SMatthew Dillon 		*errorp = EINVAL;
700243ca327SMatthew Dillon 		return(NULL);
701243ca327SMatthew Dillon 	}
70217dd83bcSMatthew Dillon 	bytes = HAMMER_HEAD_DOALIGN(mrechd.rec_size);
70317dd83bcSMatthew Dillon 	assert(bytes >= sizeof(mrechd));
704243ca327SMatthew Dillon 	mrec = malloc(bytes);
70517dd83bcSMatthew Dillon 	mrec->head = mrechd;
70617dd83bcSMatthew Dillon 
707243ca327SMatthew Dillon 	while (n < bytes) {
708243ca327SMatthew Dillon 		i = read(fdin, (char *)mrec + n, bytes - n);
709243ca327SMatthew Dillon 		if (i <= 0)
710243ca327SMatthew Dillon 			break;
711243ca327SMatthew Dillon 		n += i;
712243ca327SMatthew Dillon 	}
713243ca327SMatthew Dillon 	if (n != bytes) {
714243ca327SMatthew Dillon 		fprintf(stderr, "read_mrecord: short read on payload\n");
715243ca327SMatthew Dillon 		*errorp = EPIPE;
716243ca327SMatthew Dillon 		return(NULL);
717243ca327SMatthew Dillon 	}
71817dd83bcSMatthew Dillon 	if (mrec->head.rec_crc !=
71917dd83bcSMatthew Dillon 	    crc32((char *)mrec + HAMMER_MREC_CRCOFF,
72017dd83bcSMatthew Dillon 		  mrec->head.rec_size - HAMMER_MREC_CRCOFF)) {
721243ca327SMatthew Dillon 		fprintf(stderr, "read_mrecord: bad CRC\n");
722243ca327SMatthew Dillon 		*errorp = EINVAL;
723243ca327SMatthew Dillon 		return(NULL);
724243ca327SMatthew Dillon 	}
725243ca327SMatthew Dillon 	*errorp = 0;
726243ca327SMatthew Dillon 	return(mrec);
727243ca327SMatthew Dillon }
728243ca327SMatthew Dillon 
729243ca327SMatthew Dillon static
730243ca327SMatthew Dillon void
73117dd83bcSMatthew Dillon write_mrecord(int fdout, u_int32_t type, hammer_ioc_mrecord_any_t mrec,
73217dd83bcSMatthew Dillon 	      int bytes)
733243ca327SMatthew Dillon {
73417dd83bcSMatthew Dillon 	char zbuf[HAMMER_HEAD_ALIGN];
73517dd83bcSMatthew Dillon 	int pad;
736243ca327SMatthew Dillon 
73717dd83bcSMatthew Dillon 	pad = HAMMER_HEAD_DOALIGN(bytes) - bytes;
73817dd83bcSMatthew Dillon 
73917dd83bcSMatthew Dillon 	assert(bytes >= (int)sizeof(mrec->head));
74017dd83bcSMatthew Dillon 	bzero(&mrec->head, sizeof(mrec->head));
74117dd83bcSMatthew Dillon 	mrec->head.signature = HAMMER_IOC_MIRROR_SIGNATURE;
74217dd83bcSMatthew Dillon 	mrec->head.type = type;
74317dd83bcSMatthew Dillon 	mrec->head.rec_size = bytes;
74417dd83bcSMatthew Dillon 	mrec->head.rec_crc = crc32((char *)mrec + HAMMER_MREC_CRCOFF,
74517dd83bcSMatthew Dillon 				   bytes - HAMMER_MREC_CRCOFF);
74617dd83bcSMatthew Dillon 	if (write(fdout, mrec, bytes) != bytes) {
747243ca327SMatthew Dillon 		fprintf(stderr, "write_mrecord: error %d (%s)\n",
748243ca327SMatthew Dillon 			errno, strerror(errno));
749243ca327SMatthew Dillon 		exit(1);
750243ca327SMatthew Dillon 	}
75117dd83bcSMatthew Dillon 	if (pad) {
75217dd83bcSMatthew Dillon 		bzero(zbuf, pad);
75317dd83bcSMatthew Dillon 		if (write(fdout, zbuf, pad) != pad) {
75417dd83bcSMatthew Dillon 			fprintf(stderr, "write_mrecord: error %d (%s)\n",
75517dd83bcSMatthew Dillon 				errno, strerror(errno));
75617dd83bcSMatthew Dillon 			exit(1);
75717dd83bcSMatthew Dillon 		}
75817dd83bcSMatthew Dillon 	}
759243ca327SMatthew Dillon }
760243ca327SMatthew Dillon 
761243ca327SMatthew Dillon /*
76234ebae70SMatthew Dillon  * Generate a mirroring header with the pfs information of the
76334ebae70SMatthew Dillon  * originating filesytem.
76434ebae70SMatthew Dillon  */
76534ebae70SMatthew Dillon static void
766d4e5b69bSMatthew Dillon generate_mrec_header(int fd, int fdout, int pfs_id,
76734ebae70SMatthew Dillon 		     hammer_tid_t *tid_begp, hammer_tid_t *tid_endp)
76834ebae70SMatthew Dillon {
76934ebae70SMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
77017dd83bcSMatthew Dillon 	union hammer_ioc_mrecord_any mrec_tmp;
77134ebae70SMatthew Dillon 
77234ebae70SMatthew Dillon 	bzero(&pfs, sizeof(pfs));
77317dd83bcSMatthew Dillon 	bzero(&mrec_tmp, sizeof(mrec_tmp));
774d4e5b69bSMatthew Dillon 	pfs.pfs_id = pfs_id;
77517dd83bcSMatthew Dillon 	pfs.ondisk = &mrec_tmp.pfs.pfsd;
77617dd83bcSMatthew Dillon 	pfs.bytes = sizeof(mrec_tmp.pfs.pfsd);
77734ebae70SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
77834ebae70SMatthew Dillon 		fprintf(stderr, "mirror-read: not a HAMMER fs/pseudofs!\n");
77934ebae70SMatthew Dillon 		exit(1);
78034ebae70SMatthew Dillon 	}
78134ebae70SMatthew Dillon 	if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
78234ebae70SMatthew Dillon 		fprintf(stderr, "mirror-read: HAMMER pfs version mismatch!\n");
78334ebae70SMatthew Dillon 		exit(1);
78434ebae70SMatthew Dillon 	}
78534ebae70SMatthew Dillon 
78634ebae70SMatthew Dillon 	/*
78734ebae70SMatthew Dillon 	 * sync_beg_tid - lowest TID on source after which a full history
78834ebae70SMatthew Dillon 	 *	 	  is available.
78934ebae70SMatthew Dillon 	 *
79034ebae70SMatthew Dillon 	 * sync_end_tid - highest fully synchronized TID from source.
79134ebae70SMatthew Dillon 	 */
79217dd83bcSMatthew Dillon 	if (tid_begp && *tid_begp < mrec_tmp.pfs.pfsd.sync_beg_tid)
79317dd83bcSMatthew Dillon 		*tid_begp = mrec_tmp.pfs.pfsd.sync_beg_tid;
794d4e5b69bSMatthew Dillon 	if (tid_endp)
79517dd83bcSMatthew Dillon 		*tid_endp = mrec_tmp.pfs.pfsd.sync_end_tid;
79617dd83bcSMatthew Dillon 	mrec_tmp.pfs.version = pfs.version;
797243ca327SMatthew Dillon 	write_mrecord(fdout, HAMMER_MREC_TYPE_PFSD,
79817dd83bcSMatthew Dillon 		      &mrec_tmp, sizeof(mrec_tmp.pfs));
79934ebae70SMatthew Dillon }
80034ebae70SMatthew Dillon 
80134ebae70SMatthew Dillon /*
80234ebae70SMatthew Dillon  * Validate the pfs information from the originating filesystem
80334ebae70SMatthew Dillon  * against the target filesystem.  shared_uuid must match.
80434ebae70SMatthew Dillon  */
80534ebae70SMatthew Dillon static void
806d4e5b69bSMatthew Dillon validate_mrec_header(int fd, int fdin, int is_target, int pfs_id,
80734ebae70SMatthew Dillon 		     hammer_tid_t *tid_begp, hammer_tid_t *tid_endp)
80834ebae70SMatthew Dillon {
80934ebae70SMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
81034ebae70SMatthew Dillon 	struct hammer_pseudofs_data pfsd;
81117dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
812243ca327SMatthew Dillon 	int error;
81334ebae70SMatthew Dillon 
81434ebae70SMatthew Dillon 	/*
81534ebae70SMatthew Dillon 	 * Get the PFSD info from the target filesystem.
81634ebae70SMatthew Dillon 	 */
81734ebae70SMatthew Dillon 	bzero(&pfs, sizeof(pfs));
81834ebae70SMatthew Dillon 	bzero(&pfsd, sizeof(pfsd));
819d4e5b69bSMatthew Dillon 	pfs.pfs_id = pfs_id;
82034ebae70SMatthew Dillon 	pfs.ondisk = &pfsd;
82134ebae70SMatthew Dillon 	pfs.bytes = sizeof(pfsd);
82234ebae70SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
82334ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: not a HAMMER fs/pseudofs!\n");
82434ebae70SMatthew Dillon 		exit(1);
82534ebae70SMatthew Dillon 	}
82634ebae70SMatthew Dillon 	if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
82734ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: HAMMER pfs version mismatch!\n");
82834ebae70SMatthew Dillon 		exit(1);
82934ebae70SMatthew Dillon 	}
83034ebae70SMatthew Dillon 
831243ca327SMatthew Dillon 	mrec = read_mrecord(fdin, &error, NULL);
832243ca327SMatthew Dillon 	if (mrec == NULL) {
833243ca327SMatthew Dillon 		if (error == 0)
834243ca327SMatthew Dillon 			fprintf(stderr, "validate_mrec_header: short read\n");
83534ebae70SMatthew Dillon 		exit(1);
83634ebae70SMatthew Dillon 	}
83717dd83bcSMatthew Dillon 	if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) {
838243ca327SMatthew Dillon 		fprintf(stderr, "validate_mrec_header: did not get expected "
839243ca327SMatthew Dillon 				"PFSD record type\n");
84034ebae70SMatthew Dillon 		exit(1);
84134ebae70SMatthew Dillon 	}
84217dd83bcSMatthew Dillon 	if (mrec->head.rec_size != sizeof(mrec->pfs)) {
843243ca327SMatthew Dillon 		fprintf(stderr, "validate_mrec_header: unexpected payload "
844243ca327SMatthew Dillon 				"size\n");
84534ebae70SMatthew Dillon 		exit(1);
84634ebae70SMatthew Dillon 	}
84717dd83bcSMatthew Dillon 	if (mrec->pfs.version != pfs.version) {
848243ca327SMatthew Dillon 		fprintf(stderr, "validate_mrec_header: Version mismatch\n");
84934ebae70SMatthew Dillon 		exit(1);
85034ebae70SMatthew Dillon 	}
85134ebae70SMatthew Dillon 
85234ebae70SMatthew Dillon 	/*
85334ebae70SMatthew Dillon 	 * Whew.  Ok, is the read PFS info compatible with the target?
85434ebae70SMatthew Dillon 	 */
85517dd83bcSMatthew Dillon 	if (bcmp(&mrec->pfs.pfsd.shared_uuid, &pfsd.shared_uuid,
85617dd83bcSMatthew Dillon 		 sizeof(pfsd.shared_uuid)) != 0) {
85717dd83bcSMatthew Dillon 		fprintf(stderr,
85817dd83bcSMatthew Dillon 			"mirror-write: source and target have "
85917dd83bcSMatthew Dillon 			"different shared_uuid's!\n");
86034ebae70SMatthew Dillon 		exit(1);
86134ebae70SMatthew Dillon 	}
862d4e5b69bSMatthew Dillon 	if (is_target &&
863d4e5b69bSMatthew Dillon 	    (pfsd.mirror_flags & HAMMER_PFSD_SLAVE) == 0) {
86434ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: target must be in slave mode\n");
86534ebae70SMatthew Dillon 		exit(1);
86634ebae70SMatthew Dillon 	}
867d4e5b69bSMatthew Dillon 	if (tid_begp)
86817dd83bcSMatthew Dillon 		*tid_begp = mrec->pfs.pfsd.sync_beg_tid;
869d4e5b69bSMatthew Dillon 	if (tid_endp)
87017dd83bcSMatthew Dillon 		*tid_endp = mrec->pfs.pfsd.sync_end_tid;
871243ca327SMatthew Dillon 	free(mrec);
87234ebae70SMatthew Dillon }
87334ebae70SMatthew Dillon 
87434ebae70SMatthew Dillon static void
875d4e5b69bSMatthew Dillon update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id)
87634ebae70SMatthew Dillon {
877243ca327SMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
878243ca327SMatthew Dillon 	struct hammer_pseudofs_data pfsd;
87934ebae70SMatthew Dillon 
880243ca327SMatthew Dillon 	bzero(&pfs, sizeof(pfs));
881243ca327SMatthew Dillon 	bzero(&pfsd, sizeof(pfsd));
882d4e5b69bSMatthew Dillon 	pfs.pfs_id = pfs_id;
883243ca327SMatthew Dillon 	pfs.ondisk = &pfsd;
884243ca327SMatthew Dillon 	pfs.bytes = sizeof(pfsd);
885243ca327SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
886243ca327SMatthew Dillon 		perror("update_pfs_snapshot (read)");
887243ca327SMatthew Dillon 		exit(1);
88834ebae70SMatthew Dillon 	}
889*9c67b4d2SMatthew Dillon 	if (pfsd.sync_end_tid != snapshot_tid) {
890ddc8e722SMatthew Dillon 		pfsd.sync_end_tid = snapshot_tid;
891243ca327SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) != 0) {
892243ca327SMatthew Dillon 			perror("update_pfs_snapshot (rewrite)");
893243ca327SMatthew Dillon 			exit(1);
89434ebae70SMatthew Dillon 		}
895243ca327SMatthew Dillon 	}
896*9c67b4d2SMatthew Dillon }
897243ca327SMatthew Dillon 
89834ebae70SMatthew Dillon 
899a7fbbf91SMatthew Dillon static void
900a7fbbf91SMatthew Dillon mirror_usage(int code)
901a7fbbf91SMatthew Dillon {
902a7fbbf91SMatthew Dillon 	fprintf(stderr,
903a7fbbf91SMatthew Dillon 		"hammer mirror-read <filesystem>\n"
904a7fbbf91SMatthew Dillon 		"hammer mirror-write <filesystem>\n"
905243ca327SMatthew Dillon 		"hammer mirror-dump\n"
906a7fbbf91SMatthew Dillon 		"hammer mirror-copy [[user@]host:]fs [[user@]host:]fs\n"
907a7fbbf91SMatthew Dillon 	);
908a7fbbf91SMatthew Dillon 	exit(code);
909a7fbbf91SMatthew Dillon }
910