xref: /dflybsd-src/sbin/hammer/cmd_mirror.c (revision 17dd83bc5b3e0b38c437c50798c8c2e907f32b64)
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*17dd83bcSMatthew Dillon  * $DragonFly: src/sbin/hammer/cmd_mirror.c,v 1.7 2008/07/10 04:44:58 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,
42*17dd83bcSMatthew Dillon 			 hammer_ioc_mrecord_head_t pickup);
43*17dd83bcSMatthew Dillon static hammer_ioc_mrecord_any_t read_mrecord(int fdin, int *errorp,
44*17dd83bcSMatthew Dillon 			 hammer_ioc_mrecord_head_t pickup);
45*17dd83bcSMatthew Dillon static void write_mrecord(int fdout, u_int32_t type,
46*17dd83bcSMatthew 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 
54*17dd83bcSMatthew Dillon /*
55*17dd83bcSMatthew Dillon  * Generate a mirroring data stream from the specific source over the
56*17dd83bcSMatthew Dillon  * entire key range, but restricted to the specified transaction range.
57*17dd83bcSMatthew Dillon  *
58*17dd83bcSMatthew Dillon  * The HAMMER VFS does most of the work, we add a few new mrecord
59*17dd83bcSMatthew Dillon  * types to negotiate the TID ranges and verify that the entire
60*17dd83bcSMatthew Dillon  * stream made it to the destination.
61*17dd83bcSMatthew 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;
67*17dd83bcSMatthew Dillon 	union hammer_ioc_mrecord_any mrec_tmp;
68*17dd83bcSMatthew 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 
111*17dd83bcSMatthew Dillon 	if (ac == 2)
112*17dd83bcSMatthew Dillon 		mirror.tid_beg = strtoull(av[1], NULL, 0);
113*17dd83bcSMatthew 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 		}
136243ca327SMatthew Dillon 		if (mirror.count) {
137243ca327SMatthew Dillon 			n = write(1, mirror.ubuf, mirror.count);
138243ca327SMatthew Dillon 			if (n != mirror.count) {
139243ca327SMatthew Dillon 				fprintf(stderr, "Mirror-read %s failed: "
140243ca327SMatthew Dillon 						"short write\n",
141243ca327SMatthew Dillon 				filesystem);
142243ca327SMatthew Dillon 				exit(1);
143243ca327SMatthew Dillon 			}
144a7fbbf91SMatthew Dillon 		}
145a7fbbf91SMatthew Dillon 		mirror.key_beg = mirror.key_cur;
146243ca327SMatthew Dillon 		if (TimeoutOpt &&
147243ca327SMatthew Dillon 		    (unsigned)(time(NULL) - base_t) > (unsigned)TimeoutOpt) {
148243ca327SMatthew Dillon 			fprintf(stderr,
149243ca327SMatthew Dillon 				"Mirror-read %s interrupted by timer at"
150243ca327SMatthew Dillon 				" %016llx\n",
151243ca327SMatthew Dillon 				filesystem,
152243ca327SMatthew Dillon 				mirror.key_cur.obj_id);
153243ca327SMatthew Dillon 			interrupted = 1;
154243ca327SMatthew Dillon 			break;
155243ca327SMatthew Dillon 		}
156a7fbbf91SMatthew Dillon 	} while (mirror.count != 0);
157a7fbbf91SMatthew Dillon 
158243ca327SMatthew Dillon 	/*
159243ca327SMatthew Dillon 	 * Write out the termination sync record
160243ca327SMatthew Dillon 	 */
161*17dd83bcSMatthew Dillon 	write_mrecord(1, HAMMER_MREC_TYPE_SYNC,
162*17dd83bcSMatthew Dillon 		      &mrec_tmp, sizeof(mrec_tmp.sync));
16334ebae70SMatthew Dillon 
164243ca327SMatthew Dillon 	/*
165243ca327SMatthew Dillon 	 * If the -2 option was given (automatic when doing mirror-copy),
166243ca327SMatthew Dillon 	 * a two-way pipe is assumed and we expect a response mrec from
167243ca327SMatthew Dillon 	 * the target.
168243ca327SMatthew Dillon 	 */
169243ca327SMatthew Dillon 	if (TwoWayPipeOpt) {
170243ca327SMatthew Dillon 		mrec = read_mrecord(0, &error, NULL);
171*17dd83bcSMatthew Dillon 		if (mrec == NULL ||
172*17dd83bcSMatthew Dillon 		    mrec->head.type != HAMMER_MREC_TYPE_UPDATE ||
173*17dd83bcSMatthew Dillon 		    mrec->head.rec_size != sizeof(mrec->update)) {
174243ca327SMatthew Dillon 			fprintf(stderr, "mirror_read: Did not get final "
175243ca327SMatthew Dillon 					"acknowledgement packet from target\n");
176243ca327SMatthew Dillon 			exit(1);
177243ca327SMatthew Dillon 		}
178243ca327SMatthew Dillon 		if (interrupted) {
179243ca327SMatthew Dillon 			if (CyclePath) {
180243ca327SMatthew Dillon 				hammer_set_cycle(&mirror.key_cur, mirror.tid_beg);
181243ca327SMatthew Dillon 				fprintf(stderr, "Cyclefile %s updated for continuation\n", CyclePath);
182243ca327SMatthew Dillon 			}
183243ca327SMatthew Dillon 		} else {
184*17dd83bcSMatthew Dillon 			sync_tid = mrec->update.tid;
185243ca327SMatthew Dillon 			if (CyclePath) {
186243ca327SMatthew Dillon 				hammer_key_beg_init(&mirror.key_beg);
187243ca327SMatthew Dillon 				hammer_set_cycle(&mirror.key_beg, sync_tid);
188243ca327SMatthew Dillon 				fprintf(stderr, "Cyclefile %s updated to 0x%016llx\n",
189243ca327SMatthew Dillon 					CyclePath, sync_tid);
190243ca327SMatthew Dillon 			} else {
191243ca327SMatthew Dillon 				fprintf(stderr, "Source can update synctid "
192243ca327SMatthew Dillon 						"to 0x%016llx\n",
193243ca327SMatthew Dillon 					sync_tid);
194243ca327SMatthew Dillon 			}
195243ca327SMatthew Dillon 		}
196243ca327SMatthew Dillon 	} else if (CyclePath) {
197243ca327SMatthew Dillon 		/* NOTE! mirror.tid_beg cannot be updated */
198243ca327SMatthew Dillon 		fprintf(stderr, "Warning: cycle file (-c option) cannot be "
199243ca327SMatthew Dillon 				"fully updated unless you use mirror-copy\n");
200243ca327SMatthew Dillon 		hammer_set_cycle(&mirror.key_beg, mirror.tid_beg);
201243ca327SMatthew Dillon 	}
202a7fbbf91SMatthew Dillon 	fprintf(stderr, "Mirror-read %s succeeded\n", filesystem);
203a7fbbf91SMatthew Dillon }
204a7fbbf91SMatthew Dillon 
205*17dd83bcSMatthew Dillon /*
206*17dd83bcSMatthew Dillon  * Pipe the mirroring data stream on stdin to the HAMMER VFS, adding
207*17dd83bcSMatthew Dillon  * some additional packet types to negotiate TID ranges and to verify
208*17dd83bcSMatthew Dillon  * completion.  The HAMMER VFS does most of the work.
209*17dd83bcSMatthew Dillon  *
210*17dd83bcSMatthew Dillon  * It is important to note that the mirror.key_{beg,end} range must
211*17dd83bcSMatthew Dillon  * match the ranged used by the original.  For now both sides use
212*17dd83bcSMatthew Dillon  * range the entire key space.
213*17dd83bcSMatthew Dillon  *
214*17dd83bcSMatthew Dillon  * It is even more important that the records in the stream conform
215*17dd83bcSMatthew Dillon  * to the TID range also supplied in the stream.  The HAMMER VFS will
216*17dd83bcSMatthew Dillon  * use the REC, PASS, and SKIP record types to track the portions of
217*17dd83bcSMatthew Dillon  * the B-Tree being scanned in order to be able to proactively delete
218*17dd83bcSMatthew Dillon  * records on the target within those active areas that are not mentioned
219*17dd83bcSMatthew Dillon  * by the source.
220*17dd83bcSMatthew Dillon  *
221*17dd83bcSMatthew Dillon  * The mirror.key_cur field is used by the VFS to do this tracking.  It
222*17dd83bcSMatthew Dillon  * must be initialized to key_beg but then is persistently updated by
223*17dd83bcSMatthew Dillon  * the HAMMER VFS on each successive ioctl() call.  If you blow up this
224*17dd83bcSMatthew Dillon  * field you will blow up the mirror target, possibly to the point of
225*17dd83bcSMatthew Dillon  * deleting everything.  As a safety measure the HAMMER VFS simply marks
226*17dd83bcSMatthew Dillon  * the records that the source has destroyed as deleted on the target,
227*17dd83bcSMatthew Dillon  * and normal pruning operations will deal with their final disposition
228*17dd83bcSMatthew Dillon  * at some later time.
229*17dd83bcSMatthew Dillon  */
230a7fbbf91SMatthew Dillon void
231a7fbbf91SMatthew Dillon hammer_cmd_mirror_write(char **av, int ac)
232a7fbbf91SMatthew Dillon {
233a7fbbf91SMatthew Dillon 	struct hammer_ioc_mirror_rw mirror;
234a7fbbf91SMatthew Dillon 	const char *filesystem;
235a7fbbf91SMatthew Dillon 	char *buf = malloc(SERIALBUF_SIZE);
236d4e5b69bSMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
237*17dd83bcSMatthew Dillon 	struct hammer_ioc_mrecord_head pickup;
238243ca327SMatthew Dillon 	struct hammer_ioc_synctid synctid;
239*17dd83bcSMatthew Dillon 	union hammer_ioc_mrecord_any mrec_tmp;
240*17dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
241243ca327SMatthew Dillon 	int error;
242243ca327SMatthew Dillon 	int fd;
243a7fbbf91SMatthew Dillon 
244a7fbbf91SMatthew Dillon 	if (ac > 2)
245a7fbbf91SMatthew Dillon 		mirror_usage(1);
246a7fbbf91SMatthew Dillon 	filesystem = av[0];
247a7fbbf91SMatthew Dillon 
248a7fbbf91SMatthew Dillon 	bzero(&mirror, sizeof(mirror));
249a7fbbf91SMatthew Dillon 	hammer_key_beg_init(&mirror.key_beg);
250a7fbbf91SMatthew Dillon 	hammer_key_end_init(&mirror.key_end);
251*17dd83bcSMatthew Dillon 	mirror.key_end = mirror.key_beg;
252a7fbbf91SMatthew Dillon 
253d4e5b69bSMatthew Dillon 	fd = getpfs(&pfs, filesystem);
254a7fbbf91SMatthew Dillon 
255243ca327SMatthew Dillon 	/*
256d4e5b69bSMatthew Dillon 	 * In two-way mode the target writes out a PFS packet first.
257d4e5b69bSMatthew Dillon 	 * The source uses our tid_end as its tid_beg by default,
258d4e5b69bSMatthew Dillon 	 * picking up where it left off.
259243ca327SMatthew Dillon 	 */
260d4e5b69bSMatthew Dillon 	mirror.tid_beg = 0;
261d4e5b69bSMatthew Dillon 	if (TwoWayPipeOpt) {
262d4e5b69bSMatthew Dillon 		generate_mrec_header(fd, 1, pfs.pfs_id,
263d4e5b69bSMatthew Dillon 				     &mirror.tid_beg, &mirror.tid_end);
264d4e5b69bSMatthew Dillon 	}
265d4e5b69bSMatthew Dillon 
266d4e5b69bSMatthew Dillon 	/*
267*17dd83bcSMatthew Dillon 	 * Read and process the PFS header.  The source informs us of
268*17dd83bcSMatthew Dillon 	 * the TID range the stream represents.
269d4e5b69bSMatthew Dillon 	 */
270d4e5b69bSMatthew Dillon 	validate_mrec_header(fd, 0, 1, pfs.pfs_id,
271d4e5b69bSMatthew Dillon 			     &mirror.tid_beg, &mirror.tid_end);
27234ebae70SMatthew Dillon 
273a7fbbf91SMatthew Dillon 	mirror.ubuf = buf;
274a7fbbf91SMatthew Dillon 	mirror.size = SERIALBUF_SIZE;
275a7fbbf91SMatthew Dillon 
276a7fbbf91SMatthew Dillon 	pickup.signature = 0;
277243ca327SMatthew Dillon 	pickup.type = 0;
278a7fbbf91SMatthew Dillon 
279243ca327SMatthew Dillon 	/*
280*17dd83bcSMatthew Dillon 	 * Read and process bulk records (REC, PASS, and SKIP types).
281*17dd83bcSMatthew Dillon 	 *
282*17dd83bcSMatthew Dillon 	 * On your life, do NOT mess with mirror.key_cur or your mirror
283*17dd83bcSMatthew Dillon 	 * target may become history.
284243ca327SMatthew Dillon 	 */
285a7fbbf91SMatthew Dillon 	for (;;) {
286a7fbbf91SMatthew Dillon 		mirror.count = 0;
287d4e5b69bSMatthew Dillon 		mirror.pfs_id = pfs.pfs_id;
288d4e5b69bSMatthew Dillon 		mirror.shared_uuid = pfs.ondisk->shared_uuid;
289a7fbbf91SMatthew Dillon 		mirror.size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup);
290a7fbbf91SMatthew Dillon 		if (mirror.size <= 0)
291a7fbbf91SMatthew Dillon 			break;
292a7fbbf91SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_MIRROR_WRITE, &mirror) < 0) {
293a7fbbf91SMatthew Dillon 			fprintf(stderr, "Mirror-write %s failed: %s\n",
294a7fbbf91SMatthew Dillon 				filesystem, strerror(errno));
295a7fbbf91SMatthew Dillon 			exit(1);
296a7fbbf91SMatthew Dillon 		}
297*17dd83bcSMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) {
298*17dd83bcSMatthew Dillon 			fprintf(stderr,
299*17dd83bcSMatthew Dillon 				"Mirror-write %s fatal error %d\n",
300*17dd83bcSMatthew Dillon 				filesystem, mirror.head.error);
301*17dd83bcSMatthew Dillon 			exit(1);
302*17dd83bcSMatthew Dillon 		}
303243ca327SMatthew Dillon #if 0
304a7fbbf91SMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_INTR) {
305a7fbbf91SMatthew Dillon 			fprintf(stderr,
306a7fbbf91SMatthew Dillon 				"Mirror-write %s interrupted by timer at"
307243ca327SMatthew Dillon 				" %016llx\n",
308a7fbbf91SMatthew Dillon 				filesystem,
309243ca327SMatthew Dillon 				mirror.key_cur.obj_id);
310a7fbbf91SMatthew Dillon 			exit(0);
311a7fbbf91SMatthew Dillon 		}
312243ca327SMatthew Dillon #endif
313a7fbbf91SMatthew Dillon 	}
314243ca327SMatthew Dillon 
315243ca327SMatthew Dillon 	/*
316243ca327SMatthew Dillon 	 * Read and process the termination sync record.
317243ca327SMatthew Dillon 	 */
318243ca327SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
319*17dd83bcSMatthew Dillon 	if (mrec == NULL ||
320*17dd83bcSMatthew Dillon 	    mrec->head.type != HAMMER_MREC_TYPE_SYNC ||
321*17dd83bcSMatthew Dillon 	    mrec->head.rec_size != sizeof(mrec->sync)) {
322243ca327SMatthew Dillon 		fprintf(stderr, "Mirror-write %s: Did not get termination "
323*17dd83bcSMatthew Dillon 				"sync record, or rec_size is wrong rt=%d\n",
324*17dd83bcSMatthew Dillon 				filesystem, mrec->head.type);
325243ca327SMatthew Dillon 	}
326*17dd83bcSMatthew Dillon 	free(mrec);
327*17dd83bcSMatthew Dillon 	mrec = NULL;
328243ca327SMatthew Dillon 
329243ca327SMatthew Dillon 	/*
330243ca327SMatthew Dillon 	 * Update the PFS info on the target so the user has visibility
331243ca327SMatthew Dillon 	 * into the new snapshot.
332243ca327SMatthew Dillon 	 */
333d4e5b69bSMatthew Dillon 	update_pfs_snapshot(fd, mirror.tid_end, pfs.pfs_id);
334243ca327SMatthew Dillon 
335243ca327SMatthew Dillon 	/*
336243ca327SMatthew Dillon 	 * Sync the target filesystem
337243ca327SMatthew Dillon 	 */
338243ca327SMatthew Dillon 	bzero(&synctid, sizeof(synctid));
339243ca327SMatthew Dillon 	synctid.op = HAMMER_SYNCTID_SYNC2;
340243ca327SMatthew Dillon 	ioctl(fd, HAMMERIOC_SYNCTID, &synctid);
341243ca327SMatthew Dillon 
342243ca327SMatthew Dillon 	fprintf(stderr, "Mirror-write %s: succeeded\n", filesystem);
343243ca327SMatthew Dillon 
344243ca327SMatthew Dillon 	/*
345243ca327SMatthew Dillon 	 * Report back to the originator.
346243ca327SMatthew Dillon 	 */
347243ca327SMatthew Dillon 	if (TwoWayPipeOpt) {
348*17dd83bcSMatthew Dillon 		mrec_tmp.update.tid = mirror.tid_end;
349243ca327SMatthew Dillon 		write_mrecord(1, HAMMER_MREC_TYPE_UPDATE,
350*17dd83bcSMatthew Dillon 			      &mrec_tmp, sizeof(mrec_tmp.update));
351243ca327SMatthew Dillon 	} else {
352243ca327SMatthew Dillon 		printf("Source can update synctid to 0x%016llx\n",
353243ca327SMatthew Dillon 		       mirror.tid_end);
354243ca327SMatthew Dillon 	}
355243ca327SMatthew Dillon }
356243ca327SMatthew Dillon 
357243ca327SMatthew Dillon void
358243ca327SMatthew Dillon hammer_cmd_mirror_dump(void)
359243ca327SMatthew Dillon {
360243ca327SMatthew Dillon 	char *buf = malloc(SERIALBUF_SIZE);
361*17dd83bcSMatthew Dillon 	struct hammer_ioc_mrecord_head pickup;
362*17dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
363243ca327SMatthew Dillon 	int error;
364243ca327SMatthew Dillon 	int size;
365*17dd83bcSMatthew Dillon 	int offset;
366*17dd83bcSMatthew Dillon 	int bytes;
367243ca327SMatthew Dillon 
368243ca327SMatthew Dillon 	/*
369243ca327SMatthew Dillon 	 * Read and process the PFS header
370243ca327SMatthew Dillon 	 */
371243ca327SMatthew Dillon 	pickup.signature = 0;
372243ca327SMatthew Dillon 	pickup.type = 0;
373243ca327SMatthew Dillon 
374243ca327SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
375243ca327SMatthew Dillon 
376243ca327SMatthew Dillon 	/*
377243ca327SMatthew Dillon 	 * Read and process bulk records
378243ca327SMatthew Dillon 	 */
379243ca327SMatthew Dillon 	for (;;) {
380243ca327SMatthew Dillon 		size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup);
381243ca327SMatthew Dillon 		if (size <= 0)
382243ca327SMatthew Dillon 			break;
383*17dd83bcSMatthew Dillon 		offset = 0;
384*17dd83bcSMatthew Dillon 		while (offset < size) {
385*17dd83bcSMatthew Dillon 			mrec = (void *)((char *)buf + offset);
386*17dd83bcSMatthew Dillon 			bytes = HAMMER_HEAD_DOALIGN(mrec->head.rec_size);
387*17dd83bcSMatthew Dillon 			if (offset + bytes > size) {
388*17dd83bcSMatthew Dillon 				fprintf(stderr, "Misaligned record\n");
389*17dd83bcSMatthew Dillon 				exit(1);
390*17dd83bcSMatthew Dillon 			}
391*17dd83bcSMatthew Dillon 
392*17dd83bcSMatthew Dillon 			switch(mrec->head.type) {
393*17dd83bcSMatthew Dillon 			case HAMMER_MREC_TYPE_REC:
394243ca327SMatthew Dillon 				printf("Record obj=%016llx key=%016llx "
395243ca327SMatthew Dillon 				       "rt=%02x ot=%02x\n",
396*17dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_id,
397*17dd83bcSMatthew Dillon 					mrec->rec.leaf.base.key,
398*17dd83bcSMatthew Dillon 					mrec->rec.leaf.base.rec_type,
399*17dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_type);
400243ca327SMatthew Dillon 				printf("       tids %016llx:%016llx data=%d\n",
401*17dd83bcSMatthew Dillon 					mrec->rec.leaf.base.create_tid,
402*17dd83bcSMatthew Dillon 					mrec->rec.leaf.base.delete_tid,
403*17dd83bcSMatthew Dillon 					mrec->rec.leaf.data_len);
404*17dd83bcSMatthew Dillon 				break;
405*17dd83bcSMatthew Dillon 			case HAMMER_MREC_TYPE_PASS:
406*17dd83bcSMatthew Dillon 				printf("Pass   obj=%016llx key=%016llx "
407*17dd83bcSMatthew Dillon 				       "rt=%02x ot=%02x\n",
408*17dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_id,
409*17dd83bcSMatthew Dillon 					mrec->rec.leaf.base.key,
410*17dd83bcSMatthew Dillon 					mrec->rec.leaf.base.rec_type,
411*17dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_type);
412*17dd83bcSMatthew Dillon 				printf("       tids %016llx:%016llx data=%d\n",
413*17dd83bcSMatthew Dillon 					mrec->rec.leaf.base.create_tid,
414*17dd83bcSMatthew Dillon 					mrec->rec.leaf.base.delete_tid,
415*17dd83bcSMatthew Dillon 					mrec->rec.leaf.data_len);
416*17dd83bcSMatthew Dillon 				break;
417*17dd83bcSMatthew Dillon 			case HAMMER_MREC_TYPE_SKIP:
418*17dd83bcSMatthew Dillon 				printf("Skip   obj=%016llx key=%016llx rt=%02x to\n"
419*17dd83bcSMatthew Dillon 				       "       obj=%016llx key=%016llx rt=%02x\n",
420*17dd83bcSMatthew Dillon 				       mrec->skip.skip_beg.obj_id,
421*17dd83bcSMatthew Dillon 				       mrec->skip.skip_beg.key,
422*17dd83bcSMatthew Dillon 				       mrec->skip.skip_beg.rec_type,
423*17dd83bcSMatthew Dillon 				       mrec->skip.skip_end.obj_id,
424*17dd83bcSMatthew Dillon 				       mrec->skip.skip_end.key,
425*17dd83bcSMatthew Dillon 				       mrec->skip.skip_end.rec_type);
426*17dd83bcSMatthew Dillon 			default:
427*17dd83bcSMatthew Dillon 				break;
428*17dd83bcSMatthew Dillon 			}
429*17dd83bcSMatthew Dillon 			offset += bytes;
430243ca327SMatthew Dillon 		}
431243ca327SMatthew Dillon 	}
432243ca327SMatthew Dillon 
433243ca327SMatthew Dillon 	/*
434243ca327SMatthew Dillon 	 * Read and process the termination sync record.
435243ca327SMatthew Dillon 	 */
436243ca327SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
437*17dd83bcSMatthew Dillon 	if (mrec == NULL || mrec->head.type != HAMMER_MREC_TYPE_SYNC) {
438243ca327SMatthew Dillon 		fprintf(stderr, "Mirror-dump: Did not get termination "
439243ca327SMatthew Dillon 				"sync record\n");
440243ca327SMatthew Dillon 	}
441a7fbbf91SMatthew Dillon }
442a7fbbf91SMatthew Dillon 
443a7fbbf91SMatthew Dillon void
444a7fbbf91SMatthew Dillon hammer_cmd_mirror_copy(char **av, int ac)
445a7fbbf91SMatthew Dillon {
44634ebae70SMatthew Dillon 	pid_t pid1;
44734ebae70SMatthew Dillon 	pid_t pid2;
44834ebae70SMatthew Dillon 	int fds[2];
449243ca327SMatthew Dillon 	const char *xav[16];
450243ca327SMatthew Dillon 	char tbuf[16];
45134ebae70SMatthew Dillon 	char *ptr;
452243ca327SMatthew Dillon 	int xac;
45334ebae70SMatthew Dillon 
45434ebae70SMatthew Dillon 	if (ac != 2)
45534ebae70SMatthew Dillon 		mirror_usage(1);
45634ebae70SMatthew Dillon 
45734ebae70SMatthew Dillon 	if (pipe(fds) < 0) {
45834ebae70SMatthew Dillon 		perror("pipe");
45934ebae70SMatthew Dillon 		exit(1);
46034ebae70SMatthew Dillon 	}
46134ebae70SMatthew Dillon 
462243ca327SMatthew Dillon 	TwoWayPipeOpt = 1;
463243ca327SMatthew Dillon 
46434ebae70SMatthew Dillon 	/*
46534ebae70SMatthew Dillon 	 * Source
46634ebae70SMatthew Dillon 	 */
46734ebae70SMatthew Dillon 	if ((pid1 = fork()) == 0) {
46834ebae70SMatthew Dillon 		dup2(fds[0], 0);
46934ebae70SMatthew Dillon 		dup2(fds[0], 1);
47034ebae70SMatthew Dillon 		close(fds[0]);
47134ebae70SMatthew Dillon 		close(fds[1]);
47234ebae70SMatthew Dillon 		if ((ptr = strchr(av[0], ':')) != NULL) {
47334ebae70SMatthew Dillon 			*ptr++ = 0;
474243ca327SMatthew Dillon 			xac = 0;
475243ca327SMatthew Dillon 			xav[xac++] = "ssh";
476243ca327SMatthew Dillon 			xav[xac++] = av[0];
477243ca327SMatthew Dillon 			xav[xac++] = "hammer";
478243ca327SMatthew Dillon 			if (VerboseOpt)
479243ca327SMatthew Dillon 				xav[xac++] = "-v";
480243ca327SMatthew Dillon 			xav[xac++] = "-2";
481243ca327SMatthew Dillon 			if (TimeoutOpt) {
482243ca327SMatthew Dillon 				snprintf(tbuf, sizeof(tbuf), "%d", TimeoutOpt);
483243ca327SMatthew Dillon 				xav[xac++] = "-t";
484243ca327SMatthew Dillon 				xav[xac++] = tbuf;
485243ca327SMatthew Dillon 			}
486243ca327SMatthew Dillon 			xav[xac++] = "mirror-read";
487243ca327SMatthew Dillon 			xav[xac++] = ptr;
488243ca327SMatthew Dillon 			xav[xac++] = NULL;
489243ca327SMatthew Dillon 			execv("/usr/bin/ssh", (void *)xav);
49034ebae70SMatthew Dillon 		} else {
49134ebae70SMatthew Dillon 			hammer_cmd_mirror_read(av, 1);
492243ca327SMatthew Dillon 			fflush(stdout);
493243ca327SMatthew Dillon 			fflush(stderr);
49434ebae70SMatthew Dillon 		}
49553d93cc7SMatthew Dillon 		_exit(1);
49634ebae70SMatthew Dillon 	}
49734ebae70SMatthew Dillon 
49834ebae70SMatthew Dillon 	/*
49934ebae70SMatthew Dillon 	 * Target
50034ebae70SMatthew Dillon 	 */
50134ebae70SMatthew Dillon 	if ((pid2 = fork()) == 0) {
50234ebae70SMatthew Dillon 		dup2(fds[1], 0);
50334ebae70SMatthew Dillon 		dup2(fds[1], 1);
50434ebae70SMatthew Dillon 		close(fds[0]);
50534ebae70SMatthew Dillon 		close(fds[1]);
50634ebae70SMatthew Dillon 		if ((ptr = strchr(av[1], ':')) != NULL) {
50734ebae70SMatthew Dillon 			*ptr++ = 0;
508243ca327SMatthew Dillon 			xac = 0;
509243ca327SMatthew Dillon 			xav[xac++] = "ssh";
510243ca327SMatthew Dillon 			xav[xac++] = av[1];
511243ca327SMatthew Dillon 			xav[xac++] = "hammer";
512243ca327SMatthew Dillon 			if (VerboseOpt)
513243ca327SMatthew Dillon 				xav[xac++] = "-v";
514243ca327SMatthew Dillon 			xav[xac++] = "-2";
515243ca327SMatthew Dillon 			xav[xac++] = "mirror-write";
516243ca327SMatthew Dillon 			xav[xac++] = ptr;
517243ca327SMatthew Dillon 			xav[xac++] = NULL;
518243ca327SMatthew Dillon 			execv("/usr/bin/ssh", (void *)xav);
51934ebae70SMatthew Dillon 		} else {
52034ebae70SMatthew Dillon 			hammer_cmd_mirror_write(av + 1, 1);
521243ca327SMatthew Dillon 			fflush(stdout);
522243ca327SMatthew Dillon 			fflush(stderr);
52334ebae70SMatthew Dillon 		}
52453d93cc7SMatthew Dillon 		_exit(1);
52534ebae70SMatthew Dillon 	}
52634ebae70SMatthew Dillon 	close(fds[0]);
52734ebae70SMatthew Dillon 	close(fds[1]);
52834ebae70SMatthew Dillon 
52934ebae70SMatthew Dillon 	while (waitpid(pid1, NULL, 0) <= 0)
53034ebae70SMatthew Dillon 		;
53134ebae70SMatthew Dillon 	while (waitpid(pid2, NULL, 0) <= 0)
53234ebae70SMatthew Dillon 		;
533a7fbbf91SMatthew Dillon }
534a7fbbf91SMatthew Dillon 
535243ca327SMatthew Dillon /*
536243ca327SMatthew Dillon  * Read and return multiple mrecords
537243ca327SMatthew Dillon  */
538a7fbbf91SMatthew Dillon static int
539*17dd83bcSMatthew Dillon read_mrecords(int fd, char *buf, u_int size, hammer_ioc_mrecord_head_t pickup)
540a7fbbf91SMatthew Dillon {
541*17dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
542a7fbbf91SMatthew Dillon 	u_int count;
543a7fbbf91SMatthew Dillon 	size_t n;
544a7fbbf91SMatthew Dillon 	size_t i;
545*17dd83bcSMatthew Dillon 	size_t bytes;
546a7fbbf91SMatthew Dillon 
547a7fbbf91SMatthew Dillon 	count = 0;
548a7fbbf91SMatthew Dillon 	while (size - count >= HAMMER_MREC_HEADSIZE) {
549a7fbbf91SMatthew Dillon 		/*
550a7fbbf91SMatthew Dillon 		 * Cached the record header in case we run out of buffer
551a7fbbf91SMatthew Dillon 		 * space.
552a7fbbf91SMatthew Dillon 		 */
553*17dd83bcSMatthew Dillon 		fflush(stdout);
554a7fbbf91SMatthew Dillon 		if (pickup->signature == 0) {
555a7fbbf91SMatthew Dillon 			for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) {
556a7fbbf91SMatthew Dillon 				i = read(fd, (char *)pickup + n,
557a7fbbf91SMatthew Dillon 					 HAMMER_MREC_HEADSIZE - n);
558a7fbbf91SMatthew Dillon 				if (i <= 0)
559a7fbbf91SMatthew Dillon 					break;
560a7fbbf91SMatthew Dillon 			}
561a7fbbf91SMatthew Dillon 			if (n == 0)
562a7fbbf91SMatthew Dillon 				break;
563a7fbbf91SMatthew Dillon 			if (n != HAMMER_MREC_HEADSIZE) {
564a7fbbf91SMatthew Dillon 				fprintf(stderr, "read_mrecords: short read on pipe\n");
565a7fbbf91SMatthew Dillon 				exit(1);
566a7fbbf91SMatthew Dillon 			}
567a7fbbf91SMatthew Dillon 
568a7fbbf91SMatthew Dillon 			if (pickup->signature != HAMMER_IOC_MIRROR_SIGNATURE) {
569a7fbbf91SMatthew Dillon 				fprintf(stderr, "read_mrecords: malformed record on pipe, bad signature\n");
570a7fbbf91SMatthew Dillon 				exit(1);
571a7fbbf91SMatthew Dillon 			}
572a7fbbf91SMatthew Dillon 		}
573a7fbbf91SMatthew Dillon 		if (pickup->rec_size < HAMMER_MREC_HEADSIZE ||
574*17dd83bcSMatthew Dillon 		    pickup->rec_size > sizeof(*mrec) + HAMMER_XBUFSIZE) {
575a7fbbf91SMatthew Dillon 			fprintf(stderr, "read_mrecords: malformed record on pipe, illegal rec_size\n");
576a7fbbf91SMatthew Dillon 			exit(1);
577a7fbbf91SMatthew Dillon 		}
578a7fbbf91SMatthew Dillon 
579a7fbbf91SMatthew Dillon 		/*
580a7fbbf91SMatthew Dillon 		 * Stop if we have insufficient space for the record and data.
581a7fbbf91SMatthew Dillon 		 */
582*17dd83bcSMatthew Dillon 		bytes = HAMMER_HEAD_DOALIGN(pickup->rec_size);
583*17dd83bcSMatthew Dillon 		if (size - count < bytes)
584a7fbbf91SMatthew Dillon 			break;
585a7fbbf91SMatthew Dillon 
586a7fbbf91SMatthew Dillon 		/*
587*17dd83bcSMatthew Dillon 		 * Stop if the record type is not a REC or a SKIP (the only
588*17dd83bcSMatthew Dillon 		 * two types the ioctl supports.  Other types are used only
589*17dd83bcSMatthew Dillon 		 * by the userland protocol).
590243ca327SMatthew Dillon 		 */
591*17dd83bcSMatthew Dillon 		if (pickup->type != HAMMER_MREC_TYPE_REC &&
592*17dd83bcSMatthew Dillon 		    pickup->type != HAMMER_MREC_TYPE_SKIP &&
593*17dd83bcSMatthew Dillon 		    pickup->type != HAMMER_MREC_TYPE_PASS) {
594243ca327SMatthew Dillon 			break;
595*17dd83bcSMatthew Dillon 		}
596243ca327SMatthew Dillon 
597243ca327SMatthew Dillon 		/*
598a7fbbf91SMatthew Dillon 		 * Read the remainder and clear the pickup signature.
599a7fbbf91SMatthew Dillon 		 */
600*17dd83bcSMatthew Dillon 		for (n = HAMMER_MREC_HEADSIZE; n < bytes; n += i) {
601*17dd83bcSMatthew Dillon 			i = read(fd, buf + count + n, bytes - n);
602a7fbbf91SMatthew Dillon 			if (i <= 0)
603a7fbbf91SMatthew Dillon 				break;
604a7fbbf91SMatthew Dillon 		}
605*17dd83bcSMatthew Dillon 		if (n != bytes) {
606a7fbbf91SMatthew Dillon 			fprintf(stderr, "read_mrecords: short read on pipe\n");
607a7fbbf91SMatthew Dillon 			exit(1);
608a7fbbf91SMatthew Dillon 		}
609*17dd83bcSMatthew Dillon 
610*17dd83bcSMatthew Dillon 		bcopy(pickup, buf + count, HAMMER_MREC_HEADSIZE);
611*17dd83bcSMatthew Dillon 		pickup->signature = 0;
612*17dd83bcSMatthew Dillon 		pickup->type = 0;
613*17dd83bcSMatthew Dillon 		mrec = (void *)(buf + count);
614*17dd83bcSMatthew Dillon 
615*17dd83bcSMatthew Dillon 		/*
616*17dd83bcSMatthew Dillon 		 * Validate the completed record
617*17dd83bcSMatthew Dillon 		 */
618*17dd83bcSMatthew Dillon 		if (mrec->head.rec_crc !=
619*17dd83bcSMatthew Dillon 		    crc32((char *)mrec + HAMMER_MREC_CRCOFF,
620*17dd83bcSMatthew Dillon 			  mrec->head.rec_size - HAMMER_MREC_CRCOFF)) {
621*17dd83bcSMatthew Dillon 			fprintf(stderr, "read_mrecords: malformed record "
622*17dd83bcSMatthew Dillon 					"on pipe, bad crc\n");
623*17dd83bcSMatthew Dillon 			exit(1);
624a7fbbf91SMatthew Dillon 		}
625a7fbbf91SMatthew Dillon 
626*17dd83bcSMatthew Dillon 		/*
627*17dd83bcSMatthew Dillon 		 * If its a B-Tree record validate the data crc
628*17dd83bcSMatthew Dillon 		 */
629*17dd83bcSMatthew Dillon 		if (mrec->head.type == HAMMER_MREC_TYPE_REC) {
630*17dd83bcSMatthew Dillon 			if (mrec->head.rec_size <
631*17dd83bcSMatthew Dillon 			    sizeof(mrec->rec) + mrec->rec.leaf.data_len) {
632*17dd83bcSMatthew Dillon 				fprintf(stderr,
633*17dd83bcSMatthew Dillon 					"read_mrecords: malformed record on "
634*17dd83bcSMatthew Dillon 					"pipe, illegal element data_len\n");
635*17dd83bcSMatthew Dillon 				exit(1);
636*17dd83bcSMatthew Dillon 			}
637*17dd83bcSMatthew Dillon 			if (mrec->rec.leaf.data_len &&
638*17dd83bcSMatthew Dillon 			    mrec->rec.leaf.data_offset &&
639*17dd83bcSMatthew Dillon 			    hammer_crc_test_leaf(&mrec->rec + 1, &mrec->rec.leaf) == 0) {
640*17dd83bcSMatthew Dillon 				fprintf(stderr,
641*17dd83bcSMatthew Dillon 					"read_mrecords: data_crc did not "
642*17dd83bcSMatthew Dillon 					"match data! obj=%016llx key=%016llx\n",
643*17dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_id,
644*17dd83bcSMatthew Dillon 					mrec->rec.leaf.base.key);
645*17dd83bcSMatthew Dillon 				fprintf(stderr,
646*17dd83bcSMatthew Dillon 					"continuing, but there are problems\n");
647*17dd83bcSMatthew Dillon 			}
648*17dd83bcSMatthew Dillon 		}
649*17dd83bcSMatthew Dillon 		count += bytes;
650a7fbbf91SMatthew Dillon 	}
651a7fbbf91SMatthew Dillon 	return(count);
652a7fbbf91SMatthew Dillon }
653a7fbbf91SMatthew Dillon 
65434ebae70SMatthew Dillon /*
655*17dd83bcSMatthew Dillon  * Read and return a single mrecord.
656243ca327SMatthew Dillon  */
657243ca327SMatthew Dillon static
658*17dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t
659*17dd83bcSMatthew Dillon read_mrecord(int fdin, int *errorp, hammer_ioc_mrecord_head_t pickup)
660243ca327SMatthew Dillon {
661*17dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
662*17dd83bcSMatthew Dillon 	struct hammer_ioc_mrecord_head mrechd;
663243ca327SMatthew Dillon 	size_t bytes;
664243ca327SMatthew Dillon 	size_t n;
665243ca327SMatthew Dillon 	size_t i;
666243ca327SMatthew Dillon 
667243ca327SMatthew Dillon 	if (pickup && pickup->type != 0) {
668243ca327SMatthew Dillon 		mrechd = *pickup;
669243ca327SMatthew Dillon 		pickup->signature = 0;
670243ca327SMatthew Dillon 		pickup->type = 0;
671243ca327SMatthew Dillon 		n = HAMMER_MREC_HEADSIZE;
672243ca327SMatthew Dillon 	} else {
673243ca327SMatthew Dillon 		/*
674243ca327SMatthew Dillon 		 * Read in the PFSD header from the sender.
675243ca327SMatthew Dillon 		 */
676243ca327SMatthew Dillon 		for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) {
677243ca327SMatthew Dillon 			i = read(fdin, (char *)&mrechd + n, HAMMER_MREC_HEADSIZE - n);
678243ca327SMatthew Dillon 			if (i <= 0)
679243ca327SMatthew Dillon 				break;
680243ca327SMatthew Dillon 		}
681243ca327SMatthew Dillon 		if (n == 0) {
682243ca327SMatthew Dillon 			*errorp = 0;	/* EOF */
683243ca327SMatthew Dillon 			return(NULL);
684243ca327SMatthew Dillon 		}
685243ca327SMatthew Dillon 		if (n != HAMMER_MREC_HEADSIZE) {
686243ca327SMatthew Dillon 			fprintf(stderr, "short read of mrecord header\n");
687243ca327SMatthew Dillon 			*errorp = EPIPE;
688243ca327SMatthew Dillon 			return(NULL);
689243ca327SMatthew Dillon 		}
690243ca327SMatthew Dillon 	}
691243ca327SMatthew Dillon 	if (mrechd.signature != HAMMER_IOC_MIRROR_SIGNATURE) {
692243ca327SMatthew Dillon 		fprintf(stderr, "read_mrecord: bad signature\n");
693243ca327SMatthew Dillon 		*errorp = EINVAL;
694243ca327SMatthew Dillon 		return(NULL);
695243ca327SMatthew Dillon 	}
696*17dd83bcSMatthew Dillon 	bytes = HAMMER_HEAD_DOALIGN(mrechd.rec_size);
697*17dd83bcSMatthew Dillon 	assert(bytes >= sizeof(mrechd));
698243ca327SMatthew Dillon 	mrec = malloc(bytes);
699*17dd83bcSMatthew Dillon 	mrec->head = mrechd;
700*17dd83bcSMatthew Dillon 
701243ca327SMatthew Dillon 	while (n < bytes) {
702243ca327SMatthew Dillon 		i = read(fdin, (char *)mrec + n, bytes - n);
703243ca327SMatthew Dillon 		if (i <= 0)
704243ca327SMatthew Dillon 			break;
705243ca327SMatthew Dillon 		n += i;
706243ca327SMatthew Dillon 	}
707243ca327SMatthew Dillon 	if (n != bytes) {
708243ca327SMatthew Dillon 		fprintf(stderr, "read_mrecord: short read on payload\n");
709243ca327SMatthew Dillon 		*errorp = EPIPE;
710243ca327SMatthew Dillon 		return(NULL);
711243ca327SMatthew Dillon 	}
712*17dd83bcSMatthew Dillon 	if (mrec->head.rec_crc !=
713*17dd83bcSMatthew Dillon 	    crc32((char *)mrec + HAMMER_MREC_CRCOFF,
714*17dd83bcSMatthew Dillon 		  mrec->head.rec_size - HAMMER_MREC_CRCOFF)) {
715243ca327SMatthew Dillon 		fprintf(stderr, "read_mrecord: bad CRC\n");
716243ca327SMatthew Dillon 		*errorp = EINVAL;
717243ca327SMatthew Dillon 		return(NULL);
718243ca327SMatthew Dillon 	}
719243ca327SMatthew Dillon 	*errorp = 0;
720243ca327SMatthew Dillon 	return(mrec);
721243ca327SMatthew Dillon }
722243ca327SMatthew Dillon 
723243ca327SMatthew Dillon static
724243ca327SMatthew Dillon void
725*17dd83bcSMatthew Dillon write_mrecord(int fdout, u_int32_t type, hammer_ioc_mrecord_any_t mrec,
726*17dd83bcSMatthew Dillon 	      int bytes)
727243ca327SMatthew Dillon {
728*17dd83bcSMatthew Dillon 	char zbuf[HAMMER_HEAD_ALIGN];
729*17dd83bcSMatthew Dillon 	int pad;
730243ca327SMatthew Dillon 
731*17dd83bcSMatthew Dillon 	pad = HAMMER_HEAD_DOALIGN(bytes) - bytes;
732*17dd83bcSMatthew Dillon 
733*17dd83bcSMatthew Dillon 	assert(bytes >= (int)sizeof(mrec->head));
734*17dd83bcSMatthew Dillon 	bzero(&mrec->head, sizeof(mrec->head));
735*17dd83bcSMatthew Dillon 	mrec->head.signature = HAMMER_IOC_MIRROR_SIGNATURE;
736*17dd83bcSMatthew Dillon 	mrec->head.type = type;
737*17dd83bcSMatthew Dillon 	mrec->head.rec_size = bytes;
738*17dd83bcSMatthew Dillon 	mrec->head.rec_crc = crc32((char *)mrec + HAMMER_MREC_CRCOFF,
739*17dd83bcSMatthew Dillon 				   bytes - HAMMER_MREC_CRCOFF);
740*17dd83bcSMatthew Dillon 	if (write(fdout, mrec, bytes) != bytes) {
741243ca327SMatthew Dillon 		fprintf(stderr, "write_mrecord: error %d (%s)\n",
742243ca327SMatthew Dillon 			errno, strerror(errno));
743243ca327SMatthew Dillon 		exit(1);
744243ca327SMatthew Dillon 	}
745*17dd83bcSMatthew Dillon 	if (pad) {
746*17dd83bcSMatthew Dillon 		bzero(zbuf, pad);
747*17dd83bcSMatthew Dillon 		if (write(fdout, zbuf, pad) != pad) {
748*17dd83bcSMatthew Dillon 			fprintf(stderr, "write_mrecord: error %d (%s)\n",
749*17dd83bcSMatthew Dillon 				errno, strerror(errno));
750*17dd83bcSMatthew Dillon 			exit(1);
751*17dd83bcSMatthew Dillon 		}
752*17dd83bcSMatthew Dillon 	}
753243ca327SMatthew Dillon }
754243ca327SMatthew Dillon 
755243ca327SMatthew Dillon /*
75634ebae70SMatthew Dillon  * Generate a mirroring header with the pfs information of the
75734ebae70SMatthew Dillon  * originating filesytem.
75834ebae70SMatthew Dillon  */
75934ebae70SMatthew Dillon static void
760d4e5b69bSMatthew Dillon generate_mrec_header(int fd, int fdout, int pfs_id,
76134ebae70SMatthew Dillon 		     hammer_tid_t *tid_begp, hammer_tid_t *tid_endp)
76234ebae70SMatthew Dillon {
76334ebae70SMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
764*17dd83bcSMatthew Dillon 	union hammer_ioc_mrecord_any mrec_tmp;
76534ebae70SMatthew Dillon 
76634ebae70SMatthew Dillon 	bzero(&pfs, sizeof(pfs));
767*17dd83bcSMatthew Dillon 	bzero(&mrec_tmp, sizeof(mrec_tmp));
768d4e5b69bSMatthew Dillon 	pfs.pfs_id = pfs_id;
769*17dd83bcSMatthew Dillon 	pfs.ondisk = &mrec_tmp.pfs.pfsd;
770*17dd83bcSMatthew Dillon 	pfs.bytes = sizeof(mrec_tmp.pfs.pfsd);
77134ebae70SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
77234ebae70SMatthew Dillon 		fprintf(stderr, "mirror-read: not a HAMMER fs/pseudofs!\n");
77334ebae70SMatthew Dillon 		exit(1);
77434ebae70SMatthew Dillon 	}
77534ebae70SMatthew Dillon 	if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
77634ebae70SMatthew Dillon 		fprintf(stderr, "mirror-read: HAMMER pfs version mismatch!\n");
77734ebae70SMatthew Dillon 		exit(1);
77834ebae70SMatthew Dillon 	}
77934ebae70SMatthew Dillon 
78034ebae70SMatthew Dillon 	/*
78134ebae70SMatthew Dillon 	 * sync_beg_tid - lowest TID on source after which a full history
78234ebae70SMatthew Dillon 	 *	 	  is available.
78334ebae70SMatthew Dillon 	 *
78434ebae70SMatthew Dillon 	 * sync_end_tid - highest fully synchronized TID from source.
78534ebae70SMatthew Dillon 	 */
786*17dd83bcSMatthew Dillon 	if (tid_begp && *tid_begp < mrec_tmp.pfs.pfsd.sync_beg_tid)
787*17dd83bcSMatthew Dillon 		*tid_begp = mrec_tmp.pfs.pfsd.sync_beg_tid;
788d4e5b69bSMatthew Dillon 	if (tid_endp)
789*17dd83bcSMatthew Dillon 		*tid_endp = mrec_tmp.pfs.pfsd.sync_end_tid;
790*17dd83bcSMatthew Dillon 	mrec_tmp.pfs.version = pfs.version;
791243ca327SMatthew Dillon 	write_mrecord(fdout, HAMMER_MREC_TYPE_PFSD,
792*17dd83bcSMatthew Dillon 		      &mrec_tmp, sizeof(mrec_tmp.pfs));
79334ebae70SMatthew Dillon }
79434ebae70SMatthew Dillon 
79534ebae70SMatthew Dillon /*
79634ebae70SMatthew Dillon  * Validate the pfs information from the originating filesystem
79734ebae70SMatthew Dillon  * against the target filesystem.  shared_uuid must match.
79834ebae70SMatthew Dillon  */
79934ebae70SMatthew Dillon static void
800d4e5b69bSMatthew Dillon validate_mrec_header(int fd, int fdin, int is_target, int pfs_id,
80134ebae70SMatthew Dillon 		     hammer_tid_t *tid_begp, hammer_tid_t *tid_endp)
80234ebae70SMatthew Dillon {
80334ebae70SMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
80434ebae70SMatthew Dillon 	struct hammer_pseudofs_data pfsd;
805*17dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
806243ca327SMatthew Dillon 	int error;
80734ebae70SMatthew Dillon 
80834ebae70SMatthew Dillon 	/*
80934ebae70SMatthew Dillon 	 * Get the PFSD info from the target filesystem.
81034ebae70SMatthew Dillon 	 */
81134ebae70SMatthew Dillon 	bzero(&pfs, sizeof(pfs));
81234ebae70SMatthew Dillon 	bzero(&pfsd, sizeof(pfsd));
813d4e5b69bSMatthew Dillon 	pfs.pfs_id = pfs_id;
81434ebae70SMatthew Dillon 	pfs.ondisk = &pfsd;
81534ebae70SMatthew Dillon 	pfs.bytes = sizeof(pfsd);
81634ebae70SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
81734ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: not a HAMMER fs/pseudofs!\n");
81834ebae70SMatthew Dillon 		exit(1);
81934ebae70SMatthew Dillon 	}
82034ebae70SMatthew Dillon 	if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
82134ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: HAMMER pfs version mismatch!\n");
82234ebae70SMatthew Dillon 		exit(1);
82334ebae70SMatthew Dillon 	}
82434ebae70SMatthew Dillon 
825243ca327SMatthew Dillon 	mrec = read_mrecord(fdin, &error, NULL);
826243ca327SMatthew Dillon 	if (mrec == NULL) {
827243ca327SMatthew Dillon 		if (error == 0)
828243ca327SMatthew Dillon 			fprintf(stderr, "validate_mrec_header: short read\n");
82934ebae70SMatthew Dillon 		exit(1);
83034ebae70SMatthew Dillon 	}
831*17dd83bcSMatthew Dillon 	if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) {
832243ca327SMatthew Dillon 		fprintf(stderr, "validate_mrec_header: did not get expected "
833243ca327SMatthew Dillon 				"PFSD record type\n");
83434ebae70SMatthew Dillon 		exit(1);
83534ebae70SMatthew Dillon 	}
836*17dd83bcSMatthew Dillon 	if (mrec->head.rec_size != sizeof(mrec->pfs)) {
837243ca327SMatthew Dillon 		fprintf(stderr, "validate_mrec_header: unexpected payload "
838243ca327SMatthew Dillon 				"size\n");
83934ebae70SMatthew Dillon 		exit(1);
84034ebae70SMatthew Dillon 	}
841*17dd83bcSMatthew Dillon 	if (mrec->pfs.version != pfs.version) {
842243ca327SMatthew Dillon 		fprintf(stderr, "validate_mrec_header: Version mismatch\n");
84334ebae70SMatthew Dillon 		exit(1);
84434ebae70SMatthew Dillon 	}
84534ebae70SMatthew Dillon 
84634ebae70SMatthew Dillon 	/*
84734ebae70SMatthew Dillon 	 * Whew.  Ok, is the read PFS info compatible with the target?
84834ebae70SMatthew Dillon 	 */
849*17dd83bcSMatthew Dillon 	if (bcmp(&mrec->pfs.pfsd.shared_uuid, &pfsd.shared_uuid,
850*17dd83bcSMatthew Dillon 		 sizeof(pfsd.shared_uuid)) != 0) {
851*17dd83bcSMatthew Dillon 		fprintf(stderr,
852*17dd83bcSMatthew Dillon 			"mirror-write: source and target have "
853*17dd83bcSMatthew Dillon 			"different shared_uuid's!\n");
85434ebae70SMatthew Dillon 		exit(1);
85534ebae70SMatthew Dillon 	}
856d4e5b69bSMatthew Dillon 	if (is_target &&
857d4e5b69bSMatthew Dillon 	    (pfsd.mirror_flags & HAMMER_PFSD_SLAVE) == 0) {
85834ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: target must be in slave mode\n");
85934ebae70SMatthew Dillon 		exit(1);
86034ebae70SMatthew Dillon 	}
861d4e5b69bSMatthew Dillon 	if (tid_begp)
862*17dd83bcSMatthew Dillon 		*tid_begp = mrec->pfs.pfsd.sync_beg_tid;
863d4e5b69bSMatthew Dillon 	if (tid_endp)
864*17dd83bcSMatthew Dillon 		*tid_endp = mrec->pfs.pfsd.sync_end_tid;
865243ca327SMatthew Dillon 	free(mrec);
86634ebae70SMatthew Dillon }
86734ebae70SMatthew Dillon 
86834ebae70SMatthew Dillon static void
869d4e5b69bSMatthew Dillon update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id)
87034ebae70SMatthew Dillon {
871243ca327SMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
872243ca327SMatthew Dillon 	struct hammer_pseudofs_data pfsd;
87334ebae70SMatthew Dillon 
874243ca327SMatthew Dillon 	bzero(&pfs, sizeof(pfs));
875243ca327SMatthew Dillon 	bzero(&pfsd, sizeof(pfsd));
876d4e5b69bSMatthew Dillon 	pfs.pfs_id = pfs_id;
877243ca327SMatthew Dillon 	pfs.ondisk = &pfsd;
878243ca327SMatthew Dillon 	pfs.bytes = sizeof(pfsd);
879243ca327SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
880243ca327SMatthew Dillon 		perror("update_pfs_snapshot (read)");
881243ca327SMatthew Dillon 		exit(1);
88234ebae70SMatthew Dillon 	}
883ddc8e722SMatthew Dillon 	pfsd.sync_end_tid = snapshot_tid;
884243ca327SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) != 0) {
885243ca327SMatthew Dillon 		perror("update_pfs_snapshot (rewrite)");
886243ca327SMatthew Dillon 		exit(1);
88734ebae70SMatthew Dillon 	}
888243ca327SMatthew Dillon }
889243ca327SMatthew Dillon 
89034ebae70SMatthew Dillon 
891a7fbbf91SMatthew Dillon static void
892a7fbbf91SMatthew Dillon mirror_usage(int code)
893a7fbbf91SMatthew Dillon {
894a7fbbf91SMatthew Dillon 	fprintf(stderr,
895a7fbbf91SMatthew Dillon 		"hammer mirror-read <filesystem>\n"
896a7fbbf91SMatthew Dillon 		"hammer mirror-write <filesystem>\n"
897243ca327SMatthew Dillon 		"hammer mirror-dump\n"
898a7fbbf91SMatthew Dillon 		"hammer mirror-copy [[user@]host:]fs [[user@]host:]fs\n"
899a7fbbf91SMatthew Dillon 	);
900a7fbbf91SMatthew Dillon 	exit(code);
901a7fbbf91SMatthew Dillon }
902