xref: /dflybsd-src/sbin/hammer/cmd_mirror.c (revision 243ca327ba96f43f565bb5b25d5a8ab95fcce49e)
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*243ca327SMatthew Dillon  * $DragonFly: src/sbin/hammer/cmd_mirror.c,v 1.4 2008/07/07 00:27:22 dillon Exp $
35a7fbbf91SMatthew Dillon  */
36a7fbbf91SMatthew Dillon 
37a7fbbf91SMatthew Dillon #include "hammer.h"
38a7fbbf91SMatthew Dillon 
39a7fbbf91SMatthew Dillon #define SERIALBUF_SIZE	(512 * 1024)
40a7fbbf91SMatthew Dillon 
4134ebae70SMatthew Dillon struct hammer_pfs_head {
4234ebae70SMatthew Dillon 	u_int32_t version;
4334ebae70SMatthew Dillon 	struct hammer_pseudofs_data pfsd;
4434ebae70SMatthew Dillon };
4534ebae70SMatthew Dillon 
46a7fbbf91SMatthew Dillon static int read_mrecords(int fd, char *buf, u_int size,
47a7fbbf91SMatthew Dillon 			 hammer_ioc_mrecord_t pickup);
48*243ca327SMatthew Dillon static struct hammer_ioc_mrecord *read_mrecord(int fdin, int *errorp,
49*243ca327SMatthew Dillon 			 hammer_ioc_mrecord_t pickup);
50*243ca327SMatthew Dillon static void write_mrecord(int fdout, u_int32_t type, void *payload, int bytes);
5134ebae70SMatthew Dillon static void generate_mrec_header(int fd, int fdout,
5234ebae70SMatthew Dillon 			 hammer_tid_t *tid_begp, hammer_tid_t *tid_endp);
5334ebae70SMatthew Dillon static void validate_mrec_header(int fd, int fdin,
5434ebae70SMatthew Dillon 			 hammer_tid_t *tid_begp, hammer_tid_t *tid_endp);
55*243ca327SMatthew Dillon static void update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid);
56a7fbbf91SMatthew Dillon static void mirror_usage(int code);
57a7fbbf91SMatthew Dillon 
58a7fbbf91SMatthew Dillon void
59a7fbbf91SMatthew Dillon hammer_cmd_mirror_read(char **av, int ac)
60a7fbbf91SMatthew Dillon {
61a7fbbf91SMatthew Dillon 	struct hammer_ioc_mirror_rw mirror;
62*243ca327SMatthew Dillon 	hammer_ioc_mrecord_t mrec;
63*243ca327SMatthew Dillon 	hammer_tid_t sync_tid;
64a7fbbf91SMatthew Dillon 	const char *filesystem;
65a7fbbf91SMatthew Dillon 	char *buf = malloc(SERIALBUF_SIZE);
66*243ca327SMatthew Dillon 	int interrupted = 0;
67*243ca327SMatthew Dillon 	int error;
68a7fbbf91SMatthew Dillon 	int fd;
69*243ca327SMatthew Dillon 	int n;
70*243ca327SMatthew Dillon 	time_t base_t = time(NULL);
71a7fbbf91SMatthew Dillon 
72a7fbbf91SMatthew Dillon 	if (ac > 2)
73a7fbbf91SMatthew Dillon 		mirror_usage(1);
74a7fbbf91SMatthew Dillon 	filesystem = av[0];
75a7fbbf91SMatthew Dillon 
76a7fbbf91SMatthew Dillon 	bzero(&mirror, sizeof(mirror));
77a7fbbf91SMatthew Dillon 	hammer_key_beg_init(&mirror.key_beg);
78a7fbbf91SMatthew Dillon 	hammer_key_end_init(&mirror.key_end);
79a7fbbf91SMatthew Dillon 
80a7fbbf91SMatthew Dillon 	fd = open(filesystem, O_RDONLY);
81a7fbbf91SMatthew Dillon 	if (fd < 0)
82a7fbbf91SMatthew Dillon 		err(1, "Unable to open %s", filesystem);
83a7fbbf91SMatthew Dillon 
84*243ca327SMatthew Dillon 	/*
85*243ca327SMatthew Dillon 	 * Write out the PFS header
86*243ca327SMatthew Dillon 	 */
8734ebae70SMatthew Dillon 	generate_mrec_header(fd, 1, &mirror.tid_beg, &mirror.tid_end);
88*243ca327SMatthew Dillon 	hammer_get_cycle(&mirror.key_beg, &mirror.tid_beg);
8934ebae70SMatthew Dillon 
90*243ca327SMatthew Dillon 	fprintf(stderr, "mirror-read: Mirror from %016llx to %016llx\n",
91*243ca327SMatthew Dillon 		mirror.tid_beg, mirror.tid_end);
92*243ca327SMatthew Dillon 	if (mirror.key_beg.obj_id != (int64_t)HAMMER_MIN_OBJID) {
93*243ca327SMatthew Dillon 		fprintf(stderr, "mirror-read: Resuming at object %016llx\n",
94*243ca327SMatthew Dillon 			mirror.key_beg.obj_id);
95*243ca327SMatthew Dillon 	}
96*243ca327SMatthew Dillon 
97*243ca327SMatthew Dillon 	/*
98*243ca327SMatthew Dillon 	 * Write out bulk records
99*243ca327SMatthew Dillon 	 */
100a7fbbf91SMatthew Dillon 	mirror.ubuf = buf;
101a7fbbf91SMatthew Dillon 	mirror.size = SERIALBUF_SIZE;
10234ebae70SMatthew Dillon 	if (ac == 2)
10334ebae70SMatthew Dillon 		mirror.tid_beg = strtoull(av[1], NULL, 0);
104a7fbbf91SMatthew Dillon 
105a7fbbf91SMatthew Dillon 	do {
106a7fbbf91SMatthew Dillon 		mirror.count = 0;
107a7fbbf91SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) {
108a7fbbf91SMatthew Dillon 			fprintf(stderr, "Mirror-read %s failed: %s\n",
109a7fbbf91SMatthew Dillon 				filesystem, strerror(errno));
110a7fbbf91SMatthew Dillon 			exit(1);
111a7fbbf91SMatthew Dillon 		}
112*243ca327SMatthew Dillon 		if (mirror.count) {
113*243ca327SMatthew Dillon 			n = write(1, mirror.ubuf, mirror.count);
114*243ca327SMatthew Dillon 			if (n != mirror.count) {
115*243ca327SMatthew Dillon 				fprintf(stderr, "Mirror-read %s failed: "
116*243ca327SMatthew Dillon 						"short write\n",
117*243ca327SMatthew Dillon 				filesystem);
118*243ca327SMatthew Dillon 				exit(1);
119*243ca327SMatthew Dillon 			}
120a7fbbf91SMatthew Dillon 		}
121a7fbbf91SMatthew Dillon 		mirror.key_beg = mirror.key_cur;
122*243ca327SMatthew Dillon 		if (TimeoutOpt &&
123*243ca327SMatthew Dillon 		    (unsigned)(time(NULL) - base_t) > (unsigned)TimeoutOpt) {
124*243ca327SMatthew Dillon 			fprintf(stderr,
125*243ca327SMatthew Dillon 				"Mirror-read %s interrupted by timer at"
126*243ca327SMatthew Dillon 				" %016llx\n",
127*243ca327SMatthew Dillon 				filesystem,
128*243ca327SMatthew Dillon 				mirror.key_cur.obj_id);
129*243ca327SMatthew Dillon 			interrupted = 1;
130*243ca327SMatthew Dillon 			break;
131*243ca327SMatthew Dillon 		}
132a7fbbf91SMatthew Dillon 	} while (mirror.count != 0);
133a7fbbf91SMatthew Dillon 
134*243ca327SMatthew Dillon 	/*
135*243ca327SMatthew Dillon 	 * Write out the termination sync record
136*243ca327SMatthew Dillon 	 */
137*243ca327SMatthew Dillon 	write_mrecord(1, HAMMER_MREC_TYPE_SYNC, NULL, 0);
13834ebae70SMatthew Dillon 
139*243ca327SMatthew Dillon 	/*
140*243ca327SMatthew Dillon 	 * If the -2 option was given (automatic when doing mirror-copy),
141*243ca327SMatthew Dillon 	 * a two-way pipe is assumed and we expect a response mrec from
142*243ca327SMatthew Dillon 	 * the target.
143*243ca327SMatthew Dillon 	 */
144*243ca327SMatthew Dillon 	if (TwoWayPipeOpt) {
145*243ca327SMatthew Dillon 		mrec = read_mrecord(0, &error, NULL);
146*243ca327SMatthew Dillon 		if (mrec == NULL || mrec->type != HAMMER_MREC_TYPE_UPDATE) {
147*243ca327SMatthew Dillon 			fprintf(stderr, "mirror_read: Did not get final "
148*243ca327SMatthew Dillon 					"acknowledgement packet from target\n");
149*243ca327SMatthew Dillon 			exit(1);
150*243ca327SMatthew Dillon 		}
151*243ca327SMatthew Dillon 		if (interrupted) {
152*243ca327SMatthew Dillon 			if (CyclePath) {
153*243ca327SMatthew Dillon 				hammer_set_cycle(&mirror.key_cur, mirror.tid_beg);
154*243ca327SMatthew Dillon 				fprintf(stderr, "Cyclefile %s updated for continuation\n", CyclePath);
155*243ca327SMatthew Dillon 			}
156*243ca327SMatthew Dillon 		} else {
157*243ca327SMatthew Dillon 			sync_tid = *(hammer_tid_t *)(mrec + 1);
158*243ca327SMatthew Dillon 			if (CyclePath) {
159*243ca327SMatthew Dillon 				hammer_key_beg_init(&mirror.key_beg);
160*243ca327SMatthew Dillon 				hammer_set_cycle(&mirror.key_beg, sync_tid);
161*243ca327SMatthew Dillon 				fprintf(stderr, "Cyclefile %s updated to 0x%016llx\n",
162*243ca327SMatthew Dillon 					CyclePath, sync_tid);
163*243ca327SMatthew Dillon 			} else {
164*243ca327SMatthew Dillon 				fprintf(stderr, "Source can update synctid "
165*243ca327SMatthew Dillon 						"to 0x%016llx\n",
166*243ca327SMatthew Dillon 					sync_tid);
167*243ca327SMatthew Dillon 			}
168*243ca327SMatthew Dillon 		}
169*243ca327SMatthew Dillon 	} else if (CyclePath) {
170*243ca327SMatthew Dillon 		/* NOTE! mirror.tid_beg cannot be updated */
171*243ca327SMatthew Dillon 		fprintf(stderr, "Warning: cycle file (-c option) cannot be "
172*243ca327SMatthew Dillon 				"fully updated unless you use mirror-copy\n");
173*243ca327SMatthew Dillon 		hammer_set_cycle(&mirror.key_beg, mirror.tid_beg);
174*243ca327SMatthew Dillon 	}
175a7fbbf91SMatthew Dillon 	fprintf(stderr, "Mirror-read %s succeeded\n", filesystem);
176a7fbbf91SMatthew Dillon }
177a7fbbf91SMatthew Dillon 
178a7fbbf91SMatthew Dillon void
179a7fbbf91SMatthew Dillon hammer_cmd_mirror_write(char **av, int ac)
180a7fbbf91SMatthew Dillon {
181a7fbbf91SMatthew Dillon 	struct hammer_ioc_mirror_rw mirror;
182a7fbbf91SMatthew Dillon 	const char *filesystem;
183a7fbbf91SMatthew Dillon 	char *buf = malloc(SERIALBUF_SIZE);
184a7fbbf91SMatthew Dillon 	struct hammer_ioc_mrecord pickup;
185*243ca327SMatthew Dillon 	struct hammer_ioc_synctid synctid;
186*243ca327SMatthew Dillon 	hammer_ioc_mrecord_t mrec;
187*243ca327SMatthew Dillon 	int error;
188*243ca327SMatthew Dillon 	int fd;
189a7fbbf91SMatthew Dillon 
190a7fbbf91SMatthew Dillon 	if (ac > 2)
191a7fbbf91SMatthew Dillon 		mirror_usage(1);
192a7fbbf91SMatthew Dillon 	filesystem = av[0];
193a7fbbf91SMatthew Dillon 
194a7fbbf91SMatthew Dillon 	bzero(&mirror, sizeof(mirror));
195a7fbbf91SMatthew Dillon 	hammer_key_beg_init(&mirror.key_beg);
196a7fbbf91SMatthew Dillon 	hammer_key_end_init(&mirror.key_end);
197a7fbbf91SMatthew Dillon 
198a7fbbf91SMatthew Dillon 	fd = open(filesystem, O_RDONLY);
199a7fbbf91SMatthew Dillon 	if (fd < 0)
200a7fbbf91SMatthew Dillon 		err(1, "Unable to open %s", filesystem);
201a7fbbf91SMatthew Dillon 
202*243ca327SMatthew Dillon 	/*
203*243ca327SMatthew Dillon 	 * Read and process the PFS header
204*243ca327SMatthew Dillon 	 */
20534ebae70SMatthew Dillon 	validate_mrec_header(fd, 0, &mirror.tid_beg, &mirror.tid_end);
20634ebae70SMatthew Dillon 
207a7fbbf91SMatthew Dillon 	mirror.ubuf = buf;
208a7fbbf91SMatthew Dillon 	mirror.size = SERIALBUF_SIZE;
209a7fbbf91SMatthew Dillon 
210a7fbbf91SMatthew Dillon 	pickup.signature = 0;
211*243ca327SMatthew Dillon 	pickup.type = 0;
212a7fbbf91SMatthew Dillon 
213*243ca327SMatthew Dillon 	/*
214*243ca327SMatthew Dillon 	 * Read and process bulk records
215*243ca327SMatthew Dillon 	 */
216a7fbbf91SMatthew Dillon 	for (;;) {
217a7fbbf91SMatthew Dillon 		mirror.count = 0;
218a7fbbf91SMatthew Dillon 		mirror.size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup);
219a7fbbf91SMatthew Dillon 		if (mirror.size <= 0)
220a7fbbf91SMatthew Dillon 			break;
221a7fbbf91SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_MIRROR_WRITE, &mirror) < 0) {
222a7fbbf91SMatthew Dillon 			fprintf(stderr, "Mirror-write %s failed: %s\n",
223a7fbbf91SMatthew Dillon 				filesystem, strerror(errno));
224a7fbbf91SMatthew Dillon 			exit(1);
225a7fbbf91SMatthew Dillon 		}
226*243ca327SMatthew Dillon #if 0
227a7fbbf91SMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_INTR) {
228a7fbbf91SMatthew Dillon 			fprintf(stderr,
229a7fbbf91SMatthew Dillon 				"Mirror-write %s interrupted by timer at"
230*243ca327SMatthew Dillon 				" %016llx\n",
231a7fbbf91SMatthew Dillon 				filesystem,
232*243ca327SMatthew Dillon 				mirror.key_cur.obj_id);
233a7fbbf91SMatthew Dillon 			exit(0);
234a7fbbf91SMatthew Dillon 		}
235*243ca327SMatthew Dillon #endif
236a7fbbf91SMatthew Dillon 		mirror.key_beg = mirror.key_cur;
237a7fbbf91SMatthew Dillon 	}
238*243ca327SMatthew Dillon 
239*243ca327SMatthew Dillon 	/*
240*243ca327SMatthew Dillon 	 * Read and process the termination sync record.
241*243ca327SMatthew Dillon 	 */
242*243ca327SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
243*243ca327SMatthew Dillon 	if (mrec == NULL || mrec->type != HAMMER_MREC_TYPE_SYNC) {
244*243ca327SMatthew Dillon 		fprintf(stderr, "Mirror-write %s: Did not get termination "
245*243ca327SMatthew Dillon 				"sync record\n",
246*243ca327SMatthew Dillon 				filesystem);
247*243ca327SMatthew Dillon 	}
248*243ca327SMatthew Dillon 
249*243ca327SMatthew Dillon 	/*
250*243ca327SMatthew Dillon 	 * Update the PFS info on the target so the user has visibility
251*243ca327SMatthew Dillon 	 * into the new snapshot.
252*243ca327SMatthew Dillon 	 */
253*243ca327SMatthew Dillon 	update_pfs_snapshot(fd, mirror.tid_end);
254*243ca327SMatthew Dillon 
255*243ca327SMatthew Dillon 	/*
256*243ca327SMatthew Dillon 	 * Sync the target filesystem
257*243ca327SMatthew Dillon 	 */
258*243ca327SMatthew Dillon 	bzero(&synctid, sizeof(synctid));
259*243ca327SMatthew Dillon 	synctid.op = HAMMER_SYNCTID_SYNC2;
260*243ca327SMatthew Dillon 	ioctl(fd, HAMMERIOC_SYNCTID, &synctid);
261*243ca327SMatthew Dillon 
262*243ca327SMatthew Dillon 	fprintf(stderr, "Mirror-write %s: succeeded\n", filesystem);
263*243ca327SMatthew Dillon 
264*243ca327SMatthew Dillon 	/*
265*243ca327SMatthew Dillon 	 * Report back to the originator.
266*243ca327SMatthew Dillon 	 */
267*243ca327SMatthew Dillon 	if (TwoWayPipeOpt) {
268*243ca327SMatthew Dillon 		write_mrecord(1, HAMMER_MREC_TYPE_UPDATE,
269*243ca327SMatthew Dillon 			      &mirror.tid_end, sizeof(mirror.tid_end));
270*243ca327SMatthew Dillon 	} else {
271*243ca327SMatthew Dillon 		printf("Source can update synctid to 0x%016llx\n",
272*243ca327SMatthew Dillon 		       mirror.tid_end);
273*243ca327SMatthew Dillon 	}
274*243ca327SMatthew Dillon }
275*243ca327SMatthew Dillon 
276*243ca327SMatthew Dillon void
277*243ca327SMatthew Dillon hammer_cmd_mirror_dump(void)
278*243ca327SMatthew Dillon {
279*243ca327SMatthew Dillon 	char *buf = malloc(SERIALBUF_SIZE);
280*243ca327SMatthew Dillon 	struct hammer_ioc_mrecord pickup;
281*243ca327SMatthew Dillon 	hammer_ioc_mrecord_t mrec;
282*243ca327SMatthew Dillon 	int error;
283*243ca327SMatthew Dillon 	int size;
284*243ca327SMatthew Dillon 
285*243ca327SMatthew Dillon 	/*
286*243ca327SMatthew Dillon 	 * Read and process the PFS header
287*243ca327SMatthew Dillon 	 */
288*243ca327SMatthew Dillon 	pickup.signature = 0;
289*243ca327SMatthew Dillon 	pickup.type = 0;
290*243ca327SMatthew Dillon 
291*243ca327SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
292*243ca327SMatthew Dillon 
293*243ca327SMatthew Dillon 	/*
294*243ca327SMatthew Dillon 	 * Read and process bulk records
295*243ca327SMatthew Dillon 	 */
296*243ca327SMatthew Dillon 	for (;;) {
297*243ca327SMatthew Dillon 		size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup);
298*243ca327SMatthew Dillon 		if (size <= 0)
299*243ca327SMatthew Dillon 			break;
300*243ca327SMatthew Dillon 		mrec = (void *)buf;
301*243ca327SMatthew Dillon 		while (mrec < (hammer_ioc_mrecord_t)((char *)buf + size)) {
302*243ca327SMatthew Dillon 			printf("Record obj=%016llx key=%016llx "
303*243ca327SMatthew Dillon 			       "rt=%02x ot=%02x\n",
304*243ca327SMatthew Dillon 				mrec->leaf.base.obj_id,
305*243ca327SMatthew Dillon 				mrec->leaf.base.key,
306*243ca327SMatthew Dillon 				mrec->leaf.base.rec_type,
307*243ca327SMatthew Dillon 				mrec->leaf.base.obj_type);
308*243ca327SMatthew Dillon 			printf("       tids %016llx:%016llx data=%d\n",
309*243ca327SMatthew Dillon 				mrec->leaf.base.create_tid,
310*243ca327SMatthew Dillon 				mrec->leaf.base.delete_tid,
311*243ca327SMatthew Dillon 				mrec->leaf.data_len);
312*243ca327SMatthew Dillon 			mrec = (void *)((char *)mrec + mrec->rec_size);
313*243ca327SMatthew Dillon 		}
314*243ca327SMatthew Dillon 	}
315*243ca327SMatthew Dillon 
316*243ca327SMatthew Dillon 	/*
317*243ca327SMatthew Dillon 	 * Read and process the termination sync record.
318*243ca327SMatthew Dillon 	 */
319*243ca327SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
320*243ca327SMatthew Dillon 	if (mrec == NULL || mrec->type != HAMMER_MREC_TYPE_SYNC) {
321*243ca327SMatthew Dillon 		fprintf(stderr, "Mirror-dump: Did not get termination "
322*243ca327SMatthew Dillon 				"sync record\n");
323*243ca327SMatthew Dillon 	}
324a7fbbf91SMatthew Dillon }
325a7fbbf91SMatthew Dillon 
326a7fbbf91SMatthew Dillon void
327a7fbbf91SMatthew Dillon hammer_cmd_mirror_copy(char **av, int ac)
328a7fbbf91SMatthew Dillon {
32934ebae70SMatthew Dillon 	pid_t pid1;
33034ebae70SMatthew Dillon 	pid_t pid2;
33134ebae70SMatthew Dillon 	int fds[2];
332*243ca327SMatthew Dillon 	const char *xav[16];
333*243ca327SMatthew Dillon 	char tbuf[16];
33434ebae70SMatthew Dillon 	char *ptr;
335*243ca327SMatthew Dillon 	int xac;
33634ebae70SMatthew Dillon 
33734ebae70SMatthew Dillon 	if (ac != 2)
33834ebae70SMatthew Dillon 		mirror_usage(1);
33934ebae70SMatthew Dillon 
34034ebae70SMatthew Dillon 	if (pipe(fds) < 0) {
34134ebae70SMatthew Dillon 		perror("pipe");
34234ebae70SMatthew Dillon 		exit(1);
34334ebae70SMatthew Dillon 	}
34434ebae70SMatthew Dillon 
345*243ca327SMatthew Dillon 	TwoWayPipeOpt = 1;
346*243ca327SMatthew Dillon 
34734ebae70SMatthew Dillon 	/*
34834ebae70SMatthew Dillon 	 * Source
34934ebae70SMatthew Dillon 	 */
35034ebae70SMatthew Dillon 	if ((pid1 = fork()) == 0) {
35134ebae70SMatthew Dillon 		dup2(fds[0], 0);
35234ebae70SMatthew Dillon 		dup2(fds[0], 1);
35334ebae70SMatthew Dillon 		close(fds[0]);
35434ebae70SMatthew Dillon 		close(fds[1]);
35534ebae70SMatthew Dillon 		if ((ptr = strchr(av[0], ':')) != NULL) {
35634ebae70SMatthew Dillon 			*ptr++ = 0;
357*243ca327SMatthew Dillon 			xac = 0;
358*243ca327SMatthew Dillon 			xav[xac++] = "ssh";
359*243ca327SMatthew Dillon 			xav[xac++] = av[0];
360*243ca327SMatthew Dillon 			xav[xac++] = "hammer";
361*243ca327SMatthew Dillon 			if (VerboseOpt)
362*243ca327SMatthew Dillon 				xav[xac++] = "-v";
363*243ca327SMatthew Dillon 			xav[xac++] = "-2";
364*243ca327SMatthew Dillon 			if (TimeoutOpt) {
365*243ca327SMatthew Dillon 				snprintf(tbuf, sizeof(tbuf), "%d", TimeoutOpt);
366*243ca327SMatthew Dillon 				xav[xac++] = "-t";
367*243ca327SMatthew Dillon 				xav[xac++] = tbuf;
368*243ca327SMatthew Dillon 			}
369*243ca327SMatthew Dillon 			xav[xac++] = "mirror-read";
370*243ca327SMatthew Dillon 			xav[xac++] = ptr;
371*243ca327SMatthew Dillon 			xav[xac++] = NULL;
372*243ca327SMatthew Dillon 			execv("/usr/bin/ssh", (void *)xav);
37334ebae70SMatthew Dillon 		} else {
37434ebae70SMatthew Dillon 			hammer_cmd_mirror_read(av, 1);
375*243ca327SMatthew Dillon 			fflush(stdout);
376*243ca327SMatthew Dillon 			fflush(stderr);
37734ebae70SMatthew Dillon 		}
37853d93cc7SMatthew Dillon 		_exit(1);
37934ebae70SMatthew Dillon 	}
38034ebae70SMatthew Dillon 
38134ebae70SMatthew Dillon 	/*
38234ebae70SMatthew Dillon 	 * Target
38334ebae70SMatthew Dillon 	 */
38434ebae70SMatthew Dillon 	if ((pid2 = fork()) == 0) {
38534ebae70SMatthew Dillon 		dup2(fds[1], 0);
38634ebae70SMatthew Dillon 		dup2(fds[1], 1);
38734ebae70SMatthew Dillon 		close(fds[0]);
38834ebae70SMatthew Dillon 		close(fds[1]);
38934ebae70SMatthew Dillon 		if ((ptr = strchr(av[1], ':')) != NULL) {
39034ebae70SMatthew Dillon 			*ptr++ = 0;
391*243ca327SMatthew Dillon 			xac = 0;
392*243ca327SMatthew Dillon 			xav[xac++] = "ssh";
393*243ca327SMatthew Dillon 			xav[xac++] = av[1];
394*243ca327SMatthew Dillon 			xav[xac++] = "hammer";
395*243ca327SMatthew Dillon 			if (VerboseOpt)
396*243ca327SMatthew Dillon 				xav[xac++] = "-v";
397*243ca327SMatthew Dillon 			xav[xac++] = "-2";
398*243ca327SMatthew Dillon 			xav[xac++] = "mirror-write";
399*243ca327SMatthew Dillon 			xav[xac++] = ptr;
400*243ca327SMatthew Dillon 			xav[xac++] = NULL;
401*243ca327SMatthew Dillon 			execv("/usr/bin/ssh", (void *)xav);
40234ebae70SMatthew Dillon 		} else {
40334ebae70SMatthew Dillon 			hammer_cmd_mirror_write(av + 1, 1);
404*243ca327SMatthew Dillon 			fflush(stdout);
405*243ca327SMatthew Dillon 			fflush(stderr);
40634ebae70SMatthew Dillon 		}
40753d93cc7SMatthew Dillon 		_exit(1);
40834ebae70SMatthew Dillon 	}
40934ebae70SMatthew Dillon 	close(fds[0]);
41034ebae70SMatthew Dillon 	close(fds[1]);
41134ebae70SMatthew Dillon 
41234ebae70SMatthew Dillon 	while (waitpid(pid1, NULL, 0) <= 0)
41334ebae70SMatthew Dillon 		;
41434ebae70SMatthew Dillon 	while (waitpid(pid2, NULL, 0) <= 0)
41534ebae70SMatthew Dillon 		;
416a7fbbf91SMatthew Dillon }
417a7fbbf91SMatthew Dillon 
418*243ca327SMatthew Dillon /*
419*243ca327SMatthew Dillon  * Read and return multiple mrecords
420*243ca327SMatthew Dillon  */
421a7fbbf91SMatthew Dillon static int
422a7fbbf91SMatthew Dillon read_mrecords(int fd, char *buf, u_int size, hammer_ioc_mrecord_t pickup)
423a7fbbf91SMatthew Dillon {
424a7fbbf91SMatthew Dillon 	u_int count;
425a7fbbf91SMatthew Dillon 	size_t n;
426a7fbbf91SMatthew Dillon 	size_t i;
427a7fbbf91SMatthew Dillon 
428a7fbbf91SMatthew Dillon 	count = 0;
429a7fbbf91SMatthew Dillon 	while (size - count >= HAMMER_MREC_HEADSIZE) {
430a7fbbf91SMatthew Dillon 		/*
431a7fbbf91SMatthew Dillon 		 * Cached the record header in case we run out of buffer
432a7fbbf91SMatthew Dillon 		 * space.
433a7fbbf91SMatthew Dillon 		 */
434a7fbbf91SMatthew Dillon 		if (pickup->signature == 0) {
435a7fbbf91SMatthew Dillon 			for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) {
436a7fbbf91SMatthew Dillon 				i = read(fd, (char *)pickup + n,
437a7fbbf91SMatthew Dillon 					 HAMMER_MREC_HEADSIZE - n);
438a7fbbf91SMatthew Dillon 				if (i <= 0)
439a7fbbf91SMatthew Dillon 					break;
440a7fbbf91SMatthew Dillon 			}
441a7fbbf91SMatthew Dillon 			if (n == 0)
442a7fbbf91SMatthew Dillon 				break;
443a7fbbf91SMatthew Dillon 			if (n != HAMMER_MREC_HEADSIZE) {
444a7fbbf91SMatthew Dillon 				fprintf(stderr, "read_mrecords: short read on pipe\n");
445a7fbbf91SMatthew Dillon 				exit(1);
446a7fbbf91SMatthew Dillon 			}
447a7fbbf91SMatthew Dillon 
448a7fbbf91SMatthew Dillon 			if (pickup->signature != HAMMER_IOC_MIRROR_SIGNATURE) {
449a7fbbf91SMatthew Dillon 				fprintf(stderr, "read_mrecords: malformed record on pipe, bad signature\n");
450a7fbbf91SMatthew Dillon 				exit(1);
451a7fbbf91SMatthew Dillon 			}
452a7fbbf91SMatthew Dillon 			if (pickup->rec_crc != crc32((char *)pickup + HAMMER_MREC_CRCOFF, HAMMER_MREC_HEADSIZE - HAMMER_MREC_CRCOFF)) {
453a7fbbf91SMatthew Dillon 				fprintf(stderr, "read_mrecords: malformed record on pipe, bad crc\n");
454a7fbbf91SMatthew Dillon 				exit(1);
455a7fbbf91SMatthew Dillon 			}
456a7fbbf91SMatthew Dillon 		}
457a7fbbf91SMatthew Dillon 		if (pickup->rec_size < HAMMER_MREC_HEADSIZE ||
458a7fbbf91SMatthew Dillon 		    pickup->rec_size > HAMMER_MREC_HEADSIZE + HAMMER_XBUFSIZE) {
459a7fbbf91SMatthew Dillon 			fprintf(stderr, "read_mrecords: malformed record on pipe, illegal rec_size\n");
460a7fbbf91SMatthew Dillon 			exit(1);
461a7fbbf91SMatthew Dillon 		}
462a7fbbf91SMatthew Dillon 		if (HAMMER_MREC_HEADSIZE + pickup->leaf.data_len > pickup->rec_size) {
463a7fbbf91SMatthew Dillon 			fprintf(stderr, "read_mrecords: malformed record on pipe, illegal element data_len\n");
464a7fbbf91SMatthew Dillon 			exit(1);
465a7fbbf91SMatthew Dillon 		}
466a7fbbf91SMatthew Dillon 
467a7fbbf91SMatthew Dillon 		/*
468a7fbbf91SMatthew Dillon 		 * Stop if we have insufficient space for the record and data.
469a7fbbf91SMatthew Dillon 		 */
470a7fbbf91SMatthew Dillon 		if (size - count < pickup->rec_size)
471a7fbbf91SMatthew Dillon 			break;
472a7fbbf91SMatthew Dillon 
473a7fbbf91SMatthew Dillon 		/*
474*243ca327SMatthew Dillon 		 * Stop if the record type is not HAMMER_MREC_TYPE_REC
475*243ca327SMatthew Dillon 		 */
476*243ca327SMatthew Dillon 		if (pickup->type != HAMMER_MREC_TYPE_REC)
477*243ca327SMatthew Dillon 			break;
478*243ca327SMatthew Dillon 
479*243ca327SMatthew Dillon 		/*
480a7fbbf91SMatthew Dillon 		 * Read the remainder and clear the pickup signature.
481a7fbbf91SMatthew Dillon 		 */
482a7fbbf91SMatthew Dillon 		bcopy(pickup, buf + count, HAMMER_MREC_HEADSIZE);
483a7fbbf91SMatthew Dillon 		pickup->signature = 0;
484*243ca327SMatthew Dillon 		pickup->type = 0;
485a7fbbf91SMatthew Dillon 		for (n = HAMMER_MREC_HEADSIZE; n < pickup->rec_size; n += i) {
486a7fbbf91SMatthew Dillon 			i = read(fd, buf + count + n, pickup->rec_size - n);
487a7fbbf91SMatthew Dillon 			if (i <= 0)
488a7fbbf91SMatthew Dillon 				break;
489a7fbbf91SMatthew Dillon 		}
490a7fbbf91SMatthew Dillon 		if (n != pickup->rec_size) {
491a7fbbf91SMatthew Dillon 			fprintf(stderr, "read_mrecords: short read on pipe\n");
492a7fbbf91SMatthew Dillon 			exit(1);
493a7fbbf91SMatthew Dillon 		}
494a7fbbf91SMatthew Dillon 		if (pickup->leaf.data_len && pickup->leaf.data_offset) {
495a7fbbf91SMatthew Dillon 			if (hammer_crc_test_leaf(buf + count + HAMMER_MREC_HEADSIZE, &pickup->leaf) == 0) {
496a7fbbf91SMatthew Dillon 				fprintf(stderr, "read_mrecords: data_crc did not match data! obj=%016llx key=%016llx\n", pickup->leaf.base.obj_id, pickup->leaf.base.key);
497a7fbbf91SMatthew Dillon 				fprintf(stderr, "continuing, but there are problems\n");
498a7fbbf91SMatthew Dillon 			}
499a7fbbf91SMatthew Dillon 		}
500a7fbbf91SMatthew Dillon 
501a7fbbf91SMatthew Dillon 		count += pickup->rec_size;
502a7fbbf91SMatthew Dillon 	}
503a7fbbf91SMatthew Dillon 	return(count);
504a7fbbf91SMatthew Dillon }
505a7fbbf91SMatthew Dillon 
50634ebae70SMatthew Dillon /*
507*243ca327SMatthew Dillon  * Read and return a single mrecord.  The returned mrec->rec_size will be
508*243ca327SMatthew Dillon  * adjusted to be the size of the payload.
509*243ca327SMatthew Dillon  */
510*243ca327SMatthew Dillon static
511*243ca327SMatthew Dillon struct hammer_ioc_mrecord *
512*243ca327SMatthew Dillon read_mrecord(int fdin, int *errorp, hammer_ioc_mrecord_t pickup)
513*243ca327SMatthew Dillon {
514*243ca327SMatthew Dillon 	hammer_ioc_mrecord_t mrec;
515*243ca327SMatthew Dillon 	struct hammer_ioc_mrecord mrechd;
516*243ca327SMatthew Dillon 	size_t bytes;
517*243ca327SMatthew Dillon 	size_t n;
518*243ca327SMatthew Dillon 	size_t i;
519*243ca327SMatthew Dillon 
520*243ca327SMatthew Dillon 	if (pickup && pickup->type != 0) {
521*243ca327SMatthew Dillon 		mrechd = *pickup;
522*243ca327SMatthew Dillon 		pickup->signature = 0;
523*243ca327SMatthew Dillon 		pickup->type = 0;
524*243ca327SMatthew Dillon 		n = HAMMER_MREC_HEADSIZE;
525*243ca327SMatthew Dillon 	} else {
526*243ca327SMatthew Dillon 		/*
527*243ca327SMatthew Dillon 		 * Read in the PFSD header from the sender.
528*243ca327SMatthew Dillon 		 */
529*243ca327SMatthew Dillon 		for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) {
530*243ca327SMatthew Dillon 			i = read(fdin, (char *)&mrechd + n, HAMMER_MREC_HEADSIZE - n);
531*243ca327SMatthew Dillon 			if (i <= 0)
532*243ca327SMatthew Dillon 				break;
533*243ca327SMatthew Dillon 		}
534*243ca327SMatthew Dillon 		if (n == 0) {
535*243ca327SMatthew Dillon 			*errorp = 0;	/* EOF */
536*243ca327SMatthew Dillon 			return(NULL);
537*243ca327SMatthew Dillon 		}
538*243ca327SMatthew Dillon 		if (n != HAMMER_MREC_HEADSIZE) {
539*243ca327SMatthew Dillon 			fprintf(stderr, "short read of mrecord header\n");
540*243ca327SMatthew Dillon 			*errorp = EPIPE;
541*243ca327SMatthew Dillon 			return(NULL);
542*243ca327SMatthew Dillon 		}
543*243ca327SMatthew Dillon 	}
544*243ca327SMatthew Dillon 	if (mrechd.signature != HAMMER_IOC_MIRROR_SIGNATURE) {
545*243ca327SMatthew Dillon 		fprintf(stderr, "read_mrecord: bad signature\n");
546*243ca327SMatthew Dillon 		*errorp = EINVAL;
547*243ca327SMatthew Dillon 		return(NULL);
548*243ca327SMatthew Dillon 	}
549*243ca327SMatthew Dillon 	bytes = mrechd.rec_size;
550*243ca327SMatthew Dillon 	if (bytes < HAMMER_MREC_HEADSIZE)
551*243ca327SMatthew Dillon 		bytes = (int)HAMMER_MREC_HEADSIZE;
552*243ca327SMatthew Dillon 	mrec = malloc(bytes);
553*243ca327SMatthew Dillon 	*mrec = mrechd;
554*243ca327SMatthew Dillon 	while (n < bytes) {
555*243ca327SMatthew Dillon 		i = read(fdin, (char *)mrec + n, bytes - n);
556*243ca327SMatthew Dillon 		if (i <= 0)
557*243ca327SMatthew Dillon 			break;
558*243ca327SMatthew Dillon 		n += i;
559*243ca327SMatthew Dillon 	}
560*243ca327SMatthew Dillon 	if (n != bytes) {
561*243ca327SMatthew Dillon 		fprintf(stderr, "read_mrecord: short read on payload\n");
562*243ca327SMatthew Dillon 		*errorp = EPIPE;
563*243ca327SMatthew Dillon 		return(NULL);
564*243ca327SMatthew Dillon 	}
565*243ca327SMatthew Dillon 	if (mrec->rec_crc != crc32((char *)mrec + HAMMER_MREC_CRCOFF,
566*243ca327SMatthew Dillon 				   bytes - HAMMER_MREC_CRCOFF)) {
567*243ca327SMatthew Dillon 		fprintf(stderr, "read_mrecord: bad CRC\n");
568*243ca327SMatthew Dillon 		*errorp = EINVAL;
569*243ca327SMatthew Dillon 		return(NULL);
570*243ca327SMatthew Dillon 	}
571*243ca327SMatthew Dillon 	mrec->rec_size -= HAMMER_MREC_HEADSIZE;
572*243ca327SMatthew Dillon 	*errorp = 0;
573*243ca327SMatthew Dillon 	return(mrec);
574*243ca327SMatthew Dillon }
575*243ca327SMatthew Dillon 
576*243ca327SMatthew Dillon static
577*243ca327SMatthew Dillon void
578*243ca327SMatthew Dillon write_mrecord(int fdout, u_int32_t type, void *payload, int bytes)
579*243ca327SMatthew Dillon {
580*243ca327SMatthew Dillon 	hammer_ioc_mrecord_t mrec;
581*243ca327SMatthew Dillon 
582*243ca327SMatthew Dillon 	mrec = malloc(HAMMER_MREC_HEADSIZE + bytes);
583*243ca327SMatthew Dillon 	bzero(mrec, sizeof(*mrec));
584*243ca327SMatthew Dillon 	mrec->signature = HAMMER_IOC_MIRROR_SIGNATURE;
585*243ca327SMatthew Dillon 	mrec->type = type;
586*243ca327SMatthew Dillon 	mrec->rec_size = HAMMER_MREC_HEADSIZE + bytes;
587*243ca327SMatthew Dillon 	bcopy(payload, mrec + 1, bytes);
588*243ca327SMatthew Dillon 	mrec->rec_crc = crc32((char *)mrec + HAMMER_MREC_CRCOFF,
589*243ca327SMatthew Dillon 				   mrec->rec_size - HAMMER_MREC_CRCOFF);
590*243ca327SMatthew Dillon 	if (write(fdout, mrec, mrec->rec_size) != (int)mrec->rec_size) {
591*243ca327SMatthew Dillon 		fprintf(stderr, "write_mrecord: error %d (%s)\n",
592*243ca327SMatthew Dillon 			errno, strerror(errno));
593*243ca327SMatthew Dillon 		exit(1);
594*243ca327SMatthew Dillon 	}
595*243ca327SMatthew Dillon 	free(mrec);
596*243ca327SMatthew Dillon }
597*243ca327SMatthew Dillon 
598*243ca327SMatthew Dillon /*
59934ebae70SMatthew Dillon  * Generate a mirroring header with the pfs information of the
60034ebae70SMatthew Dillon  * originating filesytem.
60134ebae70SMatthew Dillon  */
60234ebae70SMatthew Dillon static void
60334ebae70SMatthew Dillon generate_mrec_header(int fd, int fdout,
60434ebae70SMatthew Dillon 		     hammer_tid_t *tid_begp, hammer_tid_t *tid_endp)
60534ebae70SMatthew Dillon {
60634ebae70SMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
60734ebae70SMatthew Dillon 	struct hammer_pfs_head pfs_head;
60834ebae70SMatthew Dillon 
60934ebae70SMatthew Dillon 	bzero(&pfs, sizeof(pfs));
61034ebae70SMatthew Dillon 	bzero(&pfs_head, sizeof(pfs_head));
61134ebae70SMatthew Dillon 	pfs.ondisk = &pfs_head.pfsd;
61234ebae70SMatthew Dillon 	pfs.bytes = sizeof(pfs_head.pfsd);
61334ebae70SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
61434ebae70SMatthew Dillon 		fprintf(stderr, "mirror-read: not a HAMMER fs/pseudofs!\n");
61534ebae70SMatthew Dillon 		exit(1);
61634ebae70SMatthew Dillon 	}
61734ebae70SMatthew Dillon 	if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
61834ebae70SMatthew Dillon 		fprintf(stderr, "mirror-read: HAMMER pfs version mismatch!\n");
61934ebae70SMatthew Dillon 		exit(1);
62034ebae70SMatthew Dillon 	}
62134ebae70SMatthew Dillon 
62234ebae70SMatthew Dillon 	/*
62334ebae70SMatthew Dillon 	 * sync_beg_tid - lowest TID on source after which a full history
62434ebae70SMatthew Dillon 	 *	 	  is available.
62534ebae70SMatthew Dillon 	 *
62634ebae70SMatthew Dillon 	 * sync_end_tid - highest fully synchronized TID from source.
62734ebae70SMatthew Dillon 	 */
62834ebae70SMatthew Dillon 	*tid_begp = pfs_head.pfsd.sync_beg_tid;
62934ebae70SMatthew Dillon 	*tid_endp = pfs_head.pfsd.sync_end_tid;
63034ebae70SMatthew Dillon 
63134ebae70SMatthew Dillon 	pfs_head.version = pfs.version;
632*243ca327SMatthew Dillon 	write_mrecord(fdout, HAMMER_MREC_TYPE_PFSD,
633*243ca327SMatthew Dillon 		      &pfs_head, sizeof(pfs_head));
63434ebae70SMatthew Dillon }
63534ebae70SMatthew Dillon 
63634ebae70SMatthew Dillon /*
63734ebae70SMatthew Dillon  * Validate the pfs information from the originating filesystem
63834ebae70SMatthew Dillon  * against the target filesystem.  shared_uuid must match.
63934ebae70SMatthew Dillon  */
64034ebae70SMatthew Dillon static void
64134ebae70SMatthew Dillon validate_mrec_header(int fd, int fdin,
64234ebae70SMatthew Dillon 		     hammer_tid_t *tid_begp, hammer_tid_t *tid_endp)
64334ebae70SMatthew Dillon {
64434ebae70SMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
645*243ca327SMatthew Dillon 	struct hammer_pfs_head *pfs_head;
64634ebae70SMatthew Dillon 	struct hammer_pseudofs_data pfsd;
647*243ca327SMatthew Dillon 	hammer_ioc_mrecord_t mrec;
64834ebae70SMatthew Dillon 	size_t bytes;
649*243ca327SMatthew Dillon 	int error;
65034ebae70SMatthew Dillon 
65134ebae70SMatthew Dillon 	/*
65234ebae70SMatthew Dillon 	 * Get the PFSD info from the target filesystem.
65334ebae70SMatthew Dillon 	 */
65434ebae70SMatthew Dillon 	bzero(&pfs, sizeof(pfs));
65534ebae70SMatthew Dillon 	bzero(&pfsd, sizeof(pfsd));
65634ebae70SMatthew Dillon 	pfs.ondisk = &pfsd;
65734ebae70SMatthew Dillon 	pfs.bytes = sizeof(pfsd);
65834ebae70SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
65934ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: not a HAMMER fs/pseudofs!\n");
66034ebae70SMatthew Dillon 		exit(1);
66134ebae70SMatthew Dillon 	}
66234ebae70SMatthew Dillon 	if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
66334ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: HAMMER pfs version mismatch!\n");
66434ebae70SMatthew Dillon 		exit(1);
66534ebae70SMatthew Dillon 	}
66634ebae70SMatthew Dillon 
667*243ca327SMatthew Dillon 	mrec = read_mrecord(fdin, &error, NULL);
668*243ca327SMatthew Dillon 	if (mrec == NULL) {
669*243ca327SMatthew Dillon 		if (error == 0)
670*243ca327SMatthew Dillon 			fprintf(stderr, "validate_mrec_header: short read\n");
67134ebae70SMatthew Dillon 		exit(1);
67234ebae70SMatthew Dillon 	}
673*243ca327SMatthew Dillon 	if (mrec->type != HAMMER_MREC_TYPE_PFSD) {
674*243ca327SMatthew Dillon 		fprintf(stderr, "validate_mrec_header: did not get expected "
675*243ca327SMatthew Dillon 				"PFSD record type\n");
67634ebae70SMatthew Dillon 		exit(1);
67734ebae70SMatthew Dillon 	}
678*243ca327SMatthew Dillon 	pfs_head = (void *)(mrec + 1);
679*243ca327SMatthew Dillon 	bytes = mrec->rec_size;	/* post-adjusted for payload */
680*243ca327SMatthew Dillon 	if (bytes != sizeof(*pfs_head)) {
681*243ca327SMatthew Dillon 		fprintf(stderr, "validate_mrec_header: unexpected payload "
682*243ca327SMatthew Dillon 				"size\n");
68334ebae70SMatthew Dillon 		exit(1);
68434ebae70SMatthew Dillon 	}
685*243ca327SMatthew Dillon 	if (pfs_head->version != pfs.version) {
686*243ca327SMatthew Dillon 		fprintf(stderr, "validate_mrec_header: Version mismatch\n");
68734ebae70SMatthew Dillon 		exit(1);
68834ebae70SMatthew Dillon 	}
68934ebae70SMatthew Dillon 
69034ebae70SMatthew Dillon 	/*
69134ebae70SMatthew Dillon 	 * Whew.  Ok, is the read PFS info compatible with the target?
69234ebae70SMatthew Dillon 	 */
693*243ca327SMatthew Dillon 	if (bcmp(&pfs_head->pfsd.shared_uuid, &pfsd.shared_uuid, sizeof(pfsd.shared_uuid)) != 0) {
69434ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: source and target have different shared_uuid's!\n");
69534ebae70SMatthew Dillon 		exit(1);
69634ebae70SMatthew Dillon 	}
69734ebae70SMatthew Dillon 	if ((pfsd.mirror_flags & HAMMER_PFSD_SLAVE) == 0) {
69834ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: target must be in slave mode\n");
69934ebae70SMatthew Dillon 		exit(1);
70034ebae70SMatthew Dillon 	}
701*243ca327SMatthew Dillon 	*tid_begp = pfs_head->pfsd.sync_beg_tid;
702*243ca327SMatthew Dillon 	*tid_endp = pfs_head->pfsd.sync_end_tid;
703*243ca327SMatthew Dillon 	free(mrec);
70434ebae70SMatthew Dillon }
70534ebae70SMatthew Dillon 
70634ebae70SMatthew Dillon static void
707*243ca327SMatthew Dillon update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid)
70834ebae70SMatthew Dillon {
709*243ca327SMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
710*243ca327SMatthew Dillon 	struct hammer_pseudofs_data pfsd;
71134ebae70SMatthew Dillon 
712*243ca327SMatthew Dillon 	bzero(&pfs, sizeof(pfs));
713*243ca327SMatthew Dillon 	bzero(&pfsd, sizeof(pfsd));
714*243ca327SMatthew Dillon 	pfs.ondisk = &pfsd;
715*243ca327SMatthew Dillon 	pfs.bytes = sizeof(pfsd);
716*243ca327SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
717*243ca327SMatthew Dillon 		perror("update_pfs_snapshot (read)");
718*243ca327SMatthew Dillon 		exit(1);
71934ebae70SMatthew Dillon 	}
720*243ca327SMatthew Dillon 	pfsd.sync_beg_tid = snapshot_tid;
721*243ca327SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) != 0) {
722*243ca327SMatthew Dillon 		perror("update_pfs_snapshot (rewrite)");
723*243ca327SMatthew Dillon 		exit(1);
72434ebae70SMatthew Dillon 	}
725*243ca327SMatthew Dillon }
726*243ca327SMatthew Dillon 
72734ebae70SMatthew Dillon 
728a7fbbf91SMatthew Dillon static void
729a7fbbf91SMatthew Dillon mirror_usage(int code)
730a7fbbf91SMatthew Dillon {
731a7fbbf91SMatthew Dillon 	fprintf(stderr,
732a7fbbf91SMatthew Dillon 		"hammer mirror-read <filesystem>\n"
733a7fbbf91SMatthew Dillon 		"hammer mirror-write <filesystem>\n"
734*243ca327SMatthew Dillon 		"hammer mirror-dump\n"
735a7fbbf91SMatthew Dillon 		"hammer mirror-copy [[user@]host:]fs [[user@]host:]fs\n"
736a7fbbf91SMatthew Dillon 	);
737a7fbbf91SMatthew Dillon 	exit(code);
738a7fbbf91SMatthew Dillon }
739