HAMMER Utilities: Sync with 60I
[dragonfly.git] / sbin / hammer / cmd_mirror.c
blobefd7e82495f55fdf6c6226577b729e8363493a5b
1 /*
2 * Copyright (c) 2008 The DragonFly Project. All rights reserved.
3 *
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
34 * $DragonFly: src/sbin/hammer/cmd_mirror.c,v 1.6 2008/07/09 10:32:30 dillon Exp $
37 #include "hammer.h"
39 #define SERIALBUF_SIZE (512 * 1024)
41 struct hammer_pfs_head {
42 u_int32_t version;
43 struct hammer_pseudofs_data pfsd;
46 static int read_mrecords(int fd, char *buf, u_int size,
47 hammer_ioc_mrecord_t pickup);
48 static struct hammer_ioc_mrecord *read_mrecord(int fdin, int *errorp,
49 hammer_ioc_mrecord_t pickup);
50 static void write_mrecord(int fdout, u_int32_t type, void *payload, int bytes);
51 static void generate_mrec_header(int fd, int fdout, int pfs_id,
52 hammer_tid_t *tid_begp, hammer_tid_t *tid_endp);
53 static void validate_mrec_header(int fd, int fdin, int is_target, int pfs_id,
54 hammer_tid_t *tid_begp, hammer_tid_t *tid_endp);
55 static void update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id);
56 static void mirror_usage(int code);
58 void
59 hammer_cmd_mirror_read(char **av, int ac)
61 struct hammer_ioc_mirror_rw mirror;
62 struct hammer_ioc_pseudofs_rw pfs;
63 hammer_ioc_mrecord_t mrec;
64 hammer_tid_t sync_tid;
65 const char *filesystem;
66 char *buf = malloc(SERIALBUF_SIZE);
67 int interrupted = 0;
68 int error;
69 int fd;
70 int n;
71 time_t base_t = time(NULL);
73 if (ac > 2)
74 mirror_usage(1);
75 filesystem = av[0];
77 bzero(&mirror, sizeof(mirror));
78 hammer_key_beg_init(&mirror.key_beg);
79 hammer_key_end_init(&mirror.key_end);
81 fd = getpfs(&pfs, filesystem);
84 * In 2-way mode the target will send us a PFS info packet
85 * first. Use the target's current snapshot TID as our default
86 * begin TID.
88 mirror.tid_beg = 0;
89 if (TwoWayPipeOpt)
90 validate_mrec_header(fd, 0, 0, pfs.pfs_id,
91 NULL, &mirror.tid_beg);
94 * Write out the PFS header, tid_beg will be updated if our PFS
95 * has a larger begin sync. tid_end is set to the latest source
96 * TID whos flush cycle has completed.
98 generate_mrec_header(fd, 1, pfs.pfs_id,
99 &mirror.tid_beg, &mirror.tid_end);
102 * A cycle file overrides the beginning TID
104 hammer_get_cycle(&mirror.key_beg, &mirror.tid_beg);
106 fprintf(stderr, "mirror-read: Mirror from %016llx to %016llx\n",
107 mirror.tid_beg, mirror.tid_end);
108 if (mirror.key_beg.obj_id != (int64_t)HAMMER_MIN_OBJID) {
109 fprintf(stderr, "mirror-read: Resuming at object %016llx\n",
110 mirror.key_beg.obj_id);
114 * Write out bulk records
116 mirror.ubuf = buf;
117 mirror.size = SERIALBUF_SIZE;
118 if (ac == 2)
119 mirror.tid_beg = strtoull(av[1], NULL, 0);
121 do {
122 mirror.count = 0;
123 mirror.pfs_id = pfs.pfs_id;
124 mirror.shared_uuid = pfs.ondisk->shared_uuid;
125 if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) {
126 fprintf(stderr, "Mirror-read %s failed: %s\n",
127 filesystem, strerror(errno));
128 exit(1);
130 if (mirror.count) {
131 n = write(1, mirror.ubuf, mirror.count);
132 if (n != mirror.count) {
133 fprintf(stderr, "Mirror-read %s failed: "
134 "short write\n",
135 filesystem);
136 exit(1);
139 mirror.key_beg = mirror.key_cur;
140 if (TimeoutOpt &&
141 (unsigned)(time(NULL) - base_t) > (unsigned)TimeoutOpt) {
142 fprintf(stderr,
143 "Mirror-read %s interrupted by timer at"
144 " %016llx\n",
145 filesystem,
146 mirror.key_cur.obj_id);
147 interrupted = 1;
148 break;
150 } while (mirror.count != 0);
153 * Write out the termination sync record
155 write_mrecord(1, HAMMER_MREC_TYPE_SYNC, NULL, 0);
158 * If the -2 option was given (automatic when doing mirror-copy),
159 * a two-way pipe is assumed and we expect a response mrec from
160 * the target.
162 if (TwoWayPipeOpt) {
163 mrec = read_mrecord(0, &error, NULL);
164 if (mrec == NULL || mrec->type != HAMMER_MREC_TYPE_UPDATE) {
165 fprintf(stderr, "mirror_read: Did not get final "
166 "acknowledgement packet from target\n");
167 exit(1);
169 if (interrupted) {
170 if (CyclePath) {
171 hammer_set_cycle(&mirror.key_cur, mirror.tid_beg);
172 fprintf(stderr, "Cyclefile %s updated for continuation\n", CyclePath);
174 } else {
175 sync_tid = *(hammer_tid_t *)(mrec + 1);
176 if (CyclePath) {
177 hammer_key_beg_init(&mirror.key_beg);
178 hammer_set_cycle(&mirror.key_beg, sync_tid);
179 fprintf(stderr, "Cyclefile %s updated to 0x%016llx\n",
180 CyclePath, sync_tid);
181 } else {
182 fprintf(stderr, "Source can update synctid "
183 "to 0x%016llx\n",
184 sync_tid);
187 } else if (CyclePath) {
188 /* NOTE! mirror.tid_beg cannot be updated */
189 fprintf(stderr, "Warning: cycle file (-c option) cannot be "
190 "fully updated unless you use mirror-copy\n");
191 hammer_set_cycle(&mirror.key_beg, mirror.tid_beg);
193 fprintf(stderr, "Mirror-read %s succeeded\n", filesystem);
196 void
197 hammer_cmd_mirror_write(char **av, int ac)
199 struct hammer_ioc_mirror_rw mirror;
200 const char *filesystem;
201 char *buf = malloc(SERIALBUF_SIZE);
202 struct hammer_ioc_pseudofs_rw pfs;
203 struct hammer_ioc_mrecord pickup;
204 struct hammer_ioc_synctid synctid;
205 hammer_ioc_mrecord_t mrec;
206 int error;
207 int fd;
209 if (ac > 2)
210 mirror_usage(1);
211 filesystem = av[0];
213 bzero(&mirror, sizeof(mirror));
214 hammer_key_beg_init(&mirror.key_beg);
215 hammer_key_end_init(&mirror.key_end);
217 fd = getpfs(&pfs, filesystem);
220 * In two-way mode the target writes out a PFS packet first.
221 * The source uses our tid_end as its tid_beg by default,
222 * picking up where it left off.
224 mirror.tid_beg = 0;
225 if (TwoWayPipeOpt) {
226 generate_mrec_header(fd, 1, pfs.pfs_id,
227 &mirror.tid_beg, &mirror.tid_end);
231 * Read and process the PFS header. The source informs of its
232 * tid_end, which we set ours too upon completion of the operation
233 * without error.
235 validate_mrec_header(fd, 0, 1, pfs.pfs_id,
236 &mirror.tid_beg, &mirror.tid_end);
238 mirror.ubuf = buf;
239 mirror.size = SERIALBUF_SIZE;
241 pickup.signature = 0;
242 pickup.type = 0;
245 * Read and process bulk records
247 for (;;) {
248 mirror.count = 0;
249 mirror.pfs_id = pfs.pfs_id;
250 mirror.shared_uuid = pfs.ondisk->shared_uuid;
251 mirror.size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup);
252 if (mirror.size <= 0)
253 break;
254 if (ioctl(fd, HAMMERIOC_MIRROR_WRITE, &mirror) < 0) {
255 fprintf(stderr, "Mirror-write %s failed: %s\n",
256 filesystem, strerror(errno));
257 exit(1);
259 #if 0
260 if (mirror.head.flags & HAMMER_IOC_HEAD_INTR) {
261 fprintf(stderr,
262 "Mirror-write %s interrupted by timer at"
263 " %016llx\n",
264 filesystem,
265 mirror.key_cur.obj_id);
266 exit(0);
268 #endif
269 mirror.key_beg = mirror.key_cur;
273 * Read and process the termination sync record.
275 mrec = read_mrecord(0, &error, &pickup);
276 if (mrec == NULL || mrec->type != HAMMER_MREC_TYPE_SYNC) {
277 fprintf(stderr, "Mirror-write %s: Did not get termination "
278 "sync record\n",
279 filesystem);
283 * Update the PFS info on the target so the user has visibility
284 * into the new snapshot.
286 update_pfs_snapshot(fd, mirror.tid_end, pfs.pfs_id);
289 * Sync the target filesystem
291 bzero(&synctid, sizeof(synctid));
292 synctid.op = HAMMER_SYNCTID_SYNC2;
293 ioctl(fd, HAMMERIOC_SYNCTID, &synctid);
295 fprintf(stderr, "Mirror-write %s: succeeded\n", filesystem);
298 * Report back to the originator.
300 if (TwoWayPipeOpt) {
301 write_mrecord(1, HAMMER_MREC_TYPE_UPDATE,
302 &mirror.tid_end, sizeof(mirror.tid_end));
303 } else {
304 printf("Source can update synctid to 0x%016llx\n",
305 mirror.tid_end);
309 void
310 hammer_cmd_mirror_dump(void)
312 char *buf = malloc(SERIALBUF_SIZE);
313 struct hammer_ioc_mrecord pickup;
314 hammer_ioc_mrecord_t mrec;
315 int error;
316 int size;
319 * Read and process the PFS header
321 pickup.signature = 0;
322 pickup.type = 0;
324 mrec = read_mrecord(0, &error, &pickup);
327 * Read and process bulk records
329 for (;;) {
330 size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup);
331 if (size <= 0)
332 break;
333 mrec = (void *)buf;
334 while (mrec < (hammer_ioc_mrecord_t)((char *)buf + size)) {
335 printf("Record obj=%016llx key=%016llx "
336 "rt=%02x ot=%02x\n",
337 mrec->leaf.base.obj_id,
338 mrec->leaf.base.key,
339 mrec->leaf.base.rec_type,
340 mrec->leaf.base.obj_type);
341 printf(" tids %016llx:%016llx data=%d\n",
342 mrec->leaf.base.create_tid,
343 mrec->leaf.base.delete_tid,
344 mrec->leaf.data_len);
345 mrec = (void *)((char *)mrec + mrec->rec_size);
350 * Read and process the termination sync record.
352 mrec = read_mrecord(0, &error, &pickup);
353 if (mrec == NULL || mrec->type != HAMMER_MREC_TYPE_SYNC) {
354 fprintf(stderr, "Mirror-dump: Did not get termination "
355 "sync record\n");
359 void
360 hammer_cmd_mirror_copy(char **av, int ac)
362 pid_t pid1;
363 pid_t pid2;
364 int fds[2];
365 const char *xav[16];
366 char tbuf[16];
367 char *ptr;
368 int xac;
370 if (ac != 2)
371 mirror_usage(1);
373 if (pipe(fds) < 0) {
374 perror("pipe");
375 exit(1);
378 TwoWayPipeOpt = 1;
381 * Source
383 if ((pid1 = fork()) == 0) {
384 dup2(fds[0], 0);
385 dup2(fds[0], 1);
386 close(fds[0]);
387 close(fds[1]);
388 if ((ptr = strchr(av[0], ':')) != NULL) {
389 *ptr++ = 0;
390 xac = 0;
391 xav[xac++] = "ssh";
392 xav[xac++] = av[0];
393 xav[xac++] = "hammer";
394 if (VerboseOpt)
395 xav[xac++] = "-v";
396 xav[xac++] = "-2";
397 if (TimeoutOpt) {
398 snprintf(tbuf, sizeof(tbuf), "%d", TimeoutOpt);
399 xav[xac++] = "-t";
400 xav[xac++] = tbuf;
402 xav[xac++] = "mirror-read";
403 xav[xac++] = ptr;
404 xav[xac++] = NULL;
405 execv("/usr/bin/ssh", (void *)xav);
406 } else {
407 hammer_cmd_mirror_read(av, 1);
408 fflush(stdout);
409 fflush(stderr);
411 _exit(1);
415 * Target
417 if ((pid2 = fork()) == 0) {
418 dup2(fds[1], 0);
419 dup2(fds[1], 1);
420 close(fds[0]);
421 close(fds[1]);
422 if ((ptr = strchr(av[1], ':')) != NULL) {
423 *ptr++ = 0;
424 xac = 0;
425 xav[xac++] = "ssh";
426 xav[xac++] = av[1];
427 xav[xac++] = "hammer";
428 if (VerboseOpt)
429 xav[xac++] = "-v";
430 xav[xac++] = "-2";
431 xav[xac++] = "mirror-write";
432 xav[xac++] = ptr;
433 xav[xac++] = NULL;
434 execv("/usr/bin/ssh", (void *)xav);
435 } else {
436 hammer_cmd_mirror_write(av + 1, 1);
437 fflush(stdout);
438 fflush(stderr);
440 _exit(1);
442 close(fds[0]);
443 close(fds[1]);
445 while (waitpid(pid1, NULL, 0) <= 0)
447 while (waitpid(pid2, NULL, 0) <= 0)
452 * Read and return multiple mrecords
454 static int
455 read_mrecords(int fd, char *buf, u_int size, hammer_ioc_mrecord_t pickup)
457 u_int count;
458 size_t n;
459 size_t i;
461 count = 0;
462 while (size - count >= HAMMER_MREC_HEADSIZE) {
464 * Cached the record header in case we run out of buffer
465 * space.
467 if (pickup->signature == 0) {
468 for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) {
469 i = read(fd, (char *)pickup + n,
470 HAMMER_MREC_HEADSIZE - n);
471 if (i <= 0)
472 break;
474 if (n == 0)
475 break;
476 if (n != HAMMER_MREC_HEADSIZE) {
477 fprintf(stderr, "read_mrecords: short read on pipe\n");
478 exit(1);
481 if (pickup->signature != HAMMER_IOC_MIRROR_SIGNATURE) {
482 fprintf(stderr, "read_mrecords: malformed record on pipe, bad signature\n");
483 exit(1);
485 if (pickup->rec_crc != crc32((char *)pickup + HAMMER_MREC_CRCOFF, HAMMER_MREC_HEADSIZE - HAMMER_MREC_CRCOFF)) {
486 fprintf(stderr, "read_mrecords: malformed record on pipe, bad crc\n");
487 exit(1);
490 if (pickup->rec_size < HAMMER_MREC_HEADSIZE ||
491 pickup->rec_size > HAMMER_MREC_HEADSIZE + HAMMER_XBUFSIZE) {
492 fprintf(stderr, "read_mrecords: malformed record on pipe, illegal rec_size\n");
493 exit(1);
495 if (HAMMER_MREC_HEADSIZE + pickup->leaf.data_len > pickup->rec_size) {
496 fprintf(stderr, "read_mrecords: malformed record on pipe, illegal element data_len\n");
497 exit(1);
501 * Stop if we have insufficient space for the record and data.
503 if (size - count < pickup->rec_size)
504 break;
507 * Stop if the record type is not HAMMER_MREC_TYPE_REC
509 if (pickup->type != HAMMER_MREC_TYPE_REC)
510 break;
513 * Read the remainder and clear the pickup signature.
515 bcopy(pickup, buf + count, HAMMER_MREC_HEADSIZE);
516 pickup->signature = 0;
517 pickup->type = 0;
518 for (n = HAMMER_MREC_HEADSIZE; n < pickup->rec_size; n += i) {
519 i = read(fd, buf + count + n, pickup->rec_size - n);
520 if (i <= 0)
521 break;
523 if (n != pickup->rec_size) {
524 fprintf(stderr, "read_mrecords: short read on pipe\n");
525 exit(1);
527 if (pickup->leaf.data_len && pickup->leaf.data_offset) {
528 if (hammer_crc_test_leaf(buf + count + HAMMER_MREC_HEADSIZE, &pickup->leaf) == 0) {
529 fprintf(stderr, "read_mrecords: data_crc did not match data! obj=%016llx key=%016llx\n", pickup->leaf.base.obj_id, pickup->leaf.base.key);
530 fprintf(stderr, "continuing, but there are problems\n");
534 count += pickup->rec_size;
536 return(count);
540 * Read and return a single mrecord. The returned mrec->rec_size will be
541 * adjusted to be the size of the payload.
543 static
544 struct hammer_ioc_mrecord *
545 read_mrecord(int fdin, int *errorp, hammer_ioc_mrecord_t pickup)
547 hammer_ioc_mrecord_t mrec;
548 struct hammer_ioc_mrecord mrechd;
549 size_t bytes;
550 size_t n;
551 size_t i;
553 if (pickup && pickup->type != 0) {
554 mrechd = *pickup;
555 pickup->signature = 0;
556 pickup->type = 0;
557 n = HAMMER_MREC_HEADSIZE;
558 } else {
560 * Read in the PFSD header from the sender.
562 for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) {
563 i = read(fdin, (char *)&mrechd + n, HAMMER_MREC_HEADSIZE - n);
564 if (i <= 0)
565 break;
567 if (n == 0) {
568 *errorp = 0; /* EOF */
569 return(NULL);
571 if (n != HAMMER_MREC_HEADSIZE) {
572 fprintf(stderr, "short read of mrecord header\n");
573 *errorp = EPIPE;
574 return(NULL);
577 if (mrechd.signature != HAMMER_IOC_MIRROR_SIGNATURE) {
578 fprintf(stderr, "read_mrecord: bad signature\n");
579 *errorp = EINVAL;
580 return(NULL);
582 bytes = mrechd.rec_size;
583 if (bytes < HAMMER_MREC_HEADSIZE)
584 bytes = (int)HAMMER_MREC_HEADSIZE;
585 mrec = malloc(bytes);
586 *mrec = mrechd;
587 while (n < bytes) {
588 i = read(fdin, (char *)mrec + n, bytes - n);
589 if (i <= 0)
590 break;
591 n += i;
593 if (n != bytes) {
594 fprintf(stderr, "read_mrecord: short read on payload\n");
595 *errorp = EPIPE;
596 return(NULL);
598 if (mrec->rec_crc != crc32((char *)mrec + HAMMER_MREC_CRCOFF,
599 bytes - HAMMER_MREC_CRCOFF)) {
600 fprintf(stderr, "read_mrecord: bad CRC\n");
601 *errorp = EINVAL;
602 return(NULL);
604 mrec->rec_size -= HAMMER_MREC_HEADSIZE;
605 *errorp = 0;
606 return(mrec);
609 static
610 void
611 write_mrecord(int fdout, u_int32_t type, void *payload, int bytes)
613 hammer_ioc_mrecord_t mrec;
615 mrec = malloc(HAMMER_MREC_HEADSIZE + bytes);
616 bzero(mrec, sizeof(*mrec));
617 mrec->signature = HAMMER_IOC_MIRROR_SIGNATURE;
618 mrec->type = type;
619 mrec->rec_size = HAMMER_MREC_HEADSIZE + bytes;
620 bcopy(payload, mrec + 1, bytes);
621 mrec->rec_crc = crc32((char *)mrec + HAMMER_MREC_CRCOFF,
622 mrec->rec_size - HAMMER_MREC_CRCOFF);
623 if (write(fdout, mrec, mrec->rec_size) != (int)mrec->rec_size) {
624 fprintf(stderr, "write_mrecord: error %d (%s)\n",
625 errno, strerror(errno));
626 exit(1);
628 free(mrec);
632 * Generate a mirroring header with the pfs information of the
633 * originating filesytem.
635 static void
636 generate_mrec_header(int fd, int fdout, int pfs_id,
637 hammer_tid_t *tid_begp, hammer_tid_t *tid_endp)
639 struct hammer_ioc_pseudofs_rw pfs;
640 struct hammer_pfs_head pfs_head;
642 bzero(&pfs, sizeof(pfs));
643 bzero(&pfs_head, sizeof(pfs_head));
644 pfs.pfs_id = pfs_id;
645 pfs.ondisk = &pfs_head.pfsd;
646 pfs.bytes = sizeof(pfs_head.pfsd);
647 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
648 fprintf(stderr, "mirror-read: not a HAMMER fs/pseudofs!\n");
649 exit(1);
651 if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
652 fprintf(stderr, "mirror-read: HAMMER pfs version mismatch!\n");
653 exit(1);
657 * sync_beg_tid - lowest TID on source after which a full history
658 * is available.
660 * sync_end_tid - highest fully synchronized TID from source.
662 if (tid_begp && *tid_begp < pfs_head.pfsd.sync_beg_tid)
663 *tid_begp = pfs_head.pfsd.sync_beg_tid;
664 if (tid_endp)
665 *tid_endp = pfs_head.pfsd.sync_end_tid;
667 pfs_head.version = pfs.version;
668 write_mrecord(fdout, HAMMER_MREC_TYPE_PFSD,
669 &pfs_head, sizeof(pfs_head));
673 * Validate the pfs information from the originating filesystem
674 * against the target filesystem. shared_uuid must match.
676 static void
677 validate_mrec_header(int fd, int fdin, int is_target, int pfs_id,
678 hammer_tid_t *tid_begp, hammer_tid_t *tid_endp)
680 struct hammer_ioc_pseudofs_rw pfs;
681 struct hammer_pfs_head *pfs_head;
682 struct hammer_pseudofs_data pfsd;
683 hammer_ioc_mrecord_t mrec;
684 size_t bytes;
685 int error;
688 * Get the PFSD info from the target filesystem.
690 bzero(&pfs, sizeof(pfs));
691 bzero(&pfsd, sizeof(pfsd));
692 pfs.pfs_id = pfs_id;
693 pfs.ondisk = &pfsd;
694 pfs.bytes = sizeof(pfsd);
695 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
696 fprintf(stderr, "mirror-write: not a HAMMER fs/pseudofs!\n");
697 exit(1);
699 if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
700 fprintf(stderr, "mirror-write: HAMMER pfs version mismatch!\n");
701 exit(1);
704 mrec = read_mrecord(fdin, &error, NULL);
705 if (mrec == NULL) {
706 if (error == 0)
707 fprintf(stderr, "validate_mrec_header: short read\n");
708 exit(1);
710 if (mrec->type != HAMMER_MREC_TYPE_PFSD) {
711 fprintf(stderr, "validate_mrec_header: did not get expected "
712 "PFSD record type\n");
713 exit(1);
715 pfs_head = (void *)(mrec + 1);
716 bytes = mrec->rec_size; /* post-adjusted for payload */
717 if (bytes != sizeof(*pfs_head)) {
718 fprintf(stderr, "validate_mrec_header: unexpected payload "
719 "size\n");
720 exit(1);
722 if (pfs_head->version != pfs.version) {
723 fprintf(stderr, "validate_mrec_header: Version mismatch\n");
724 exit(1);
728 * Whew. Ok, is the read PFS info compatible with the target?
730 if (bcmp(&pfs_head->pfsd.shared_uuid, &pfsd.shared_uuid, sizeof(pfsd.shared_uuid)) != 0) {
731 fprintf(stderr, "mirror-write: source and target have different shared_uuid's!\n");
732 exit(1);
734 if (is_target &&
735 (pfsd.mirror_flags & HAMMER_PFSD_SLAVE) == 0) {
736 fprintf(stderr, "mirror-write: target must be in slave mode\n");
737 exit(1);
739 if (tid_begp)
740 *tid_begp = pfs_head->pfsd.sync_beg_tid;
741 if (tid_endp)
742 *tid_endp = pfs_head->pfsd.sync_end_tid;
743 free(mrec);
746 static void
747 update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id)
749 struct hammer_ioc_pseudofs_rw pfs;
750 struct hammer_pseudofs_data pfsd;
752 bzero(&pfs, sizeof(pfs));
753 bzero(&pfsd, sizeof(pfsd));
754 pfs.pfs_id = pfs_id;
755 pfs.ondisk = &pfsd;
756 pfs.bytes = sizeof(pfsd);
757 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
758 perror("update_pfs_snapshot (read)");
759 exit(1);
761 pfsd.sync_end_tid = snapshot_tid;
762 if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) != 0) {
763 perror("update_pfs_snapshot (rewrite)");
764 exit(1);
769 static void
770 mirror_usage(int code)
772 fprintf(stderr,
773 "hammer mirror-read <filesystem>\n"
774 "hammer mirror-write <filesystem>\n"
775 "hammer mirror-dump\n"
776 "hammer mirror-copy [[user@]host:]fs [[user@]host:]fs\n"
778 exit(code);