2 * Copyright (c) 2008 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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
34 * $DragonFly: src/sbin/hammer/cmd_mirror.c,v 1.6 2008/07/09 10:32:30 dillon Exp $
39 #define SERIALBUF_SIZE (512 * 1024)
41 struct hammer_pfs_head
{
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
);
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
);
71 time_t base_t
= time(NULL
);
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
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
117 mirror
.size
= SERIALBUF_SIZE
;
119 mirror
.tid_beg
= strtoull(av
[1], NULL
, 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
));
131 n
= write(1, mirror
.ubuf
, mirror
.count
);
132 if (n
!= mirror
.count
) {
133 fprintf(stderr
, "Mirror-read %s failed: "
139 mirror
.key_beg
= mirror
.key_cur
;
141 (unsigned)(time(NULL
) - base_t
) > (unsigned)TimeoutOpt
) {
143 "Mirror-read %s interrupted by timer at"
146 mirror
.key_cur
.obj_id
);
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
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");
171 hammer_set_cycle(&mirror
.key_cur
, mirror
.tid_beg
);
172 fprintf(stderr
, "Cyclefile %s updated for continuation\n", CyclePath
);
175 sync_tid
= *(hammer_tid_t
*)(mrec
+ 1);
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
);
182 fprintf(stderr
, "Source can update synctid "
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
);
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
;
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.
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
235 validate_mrec_header(fd
, 0, 1, pfs
.pfs_id
,
236 &mirror
.tid_beg
, &mirror
.tid_end
);
239 mirror
.size
= SERIALBUF_SIZE
;
241 pickup
.signature
= 0;
245 * Read and process bulk records
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)
254 if (ioctl(fd
, HAMMERIOC_MIRROR_WRITE
, &mirror
) < 0) {
255 fprintf(stderr
, "Mirror-write %s failed: %s\n",
256 filesystem
, strerror(errno
));
260 if (mirror
.head
.flags
& HAMMER_IOC_HEAD_INTR
) {
262 "Mirror-write %s interrupted by timer at"
265 mirror
.key_cur
.obj_id
);
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 "
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.
301 write_mrecord(1, HAMMER_MREC_TYPE_UPDATE
,
302 &mirror
.tid_end
, sizeof(mirror
.tid_end
));
304 printf("Source can update synctid to 0x%016llx\n",
310 hammer_cmd_mirror_dump(void)
312 char *buf
= malloc(SERIALBUF_SIZE
);
313 struct hammer_ioc_mrecord pickup
;
314 hammer_ioc_mrecord_t mrec
;
319 * Read and process the PFS header
321 pickup
.signature
= 0;
324 mrec
= read_mrecord(0, &error
, &pickup
);
327 * Read and process bulk records
330 size
= read_mrecords(0, buf
, SERIALBUF_SIZE
, &pickup
);
334 while (mrec
< (hammer_ioc_mrecord_t
)((char *)buf
+ size
)) {
335 printf("Record obj=%016llx key=%016llx "
337 mrec
->leaf
.base
.obj_id
,
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 "
360 hammer_cmd_mirror_copy(char **av
, int ac
)
383 if ((pid1
= fork()) == 0) {
388 if ((ptr
= strchr(av
[0], ':')) != NULL
) {
393 xav
[xac
++] = "hammer";
398 snprintf(tbuf
, sizeof(tbuf
), "%d", TimeoutOpt
);
402 xav
[xac
++] = "mirror-read";
405 execv("/usr/bin/ssh", (void *)xav
);
407 hammer_cmd_mirror_read(av
, 1);
417 if ((pid2
= fork()) == 0) {
422 if ((ptr
= strchr(av
[1], ':')) != NULL
) {
427 xav
[xac
++] = "hammer";
431 xav
[xac
++] = "mirror-write";
434 execv("/usr/bin/ssh", (void *)xav
);
436 hammer_cmd_mirror_write(av
+ 1, 1);
445 while (waitpid(pid1
, NULL
, 0) <= 0)
447 while (waitpid(pid2
, NULL
, 0) <= 0)
452 * Read and return multiple mrecords
455 read_mrecords(int fd
, char *buf
, u_int size
, hammer_ioc_mrecord_t pickup
)
462 while (size
- count
>= HAMMER_MREC_HEADSIZE
) {
464 * Cached the record header in case we run out of buffer
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
);
476 if (n
!= HAMMER_MREC_HEADSIZE
) {
477 fprintf(stderr
, "read_mrecords: short read on pipe\n");
481 if (pickup
->signature
!= HAMMER_IOC_MIRROR_SIGNATURE
) {
482 fprintf(stderr
, "read_mrecords: malformed record on pipe, bad signature\n");
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");
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");
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");
501 * Stop if we have insufficient space for the record and data.
503 if (size
- count
< pickup
->rec_size
)
507 * Stop if the record type is not HAMMER_MREC_TYPE_REC
509 if (pickup
->type
!= HAMMER_MREC_TYPE_REC
)
513 * Read the remainder and clear the pickup signature.
515 bcopy(pickup
, buf
+ count
, HAMMER_MREC_HEADSIZE
);
516 pickup
->signature
= 0;
518 for (n
= HAMMER_MREC_HEADSIZE
; n
< pickup
->rec_size
; n
+= i
) {
519 i
= read(fd
, buf
+ count
+ n
, pickup
->rec_size
- n
);
523 if (n
!= pickup
->rec_size
) {
524 fprintf(stderr
, "read_mrecords: short read on pipe\n");
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
;
540 * Read and return a single mrecord. The returned mrec->rec_size will be
541 * adjusted to be the size of the payload.
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
;
553 if (pickup
&& pickup
->type
!= 0) {
555 pickup
->signature
= 0;
557 n
= HAMMER_MREC_HEADSIZE
;
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
);
568 *errorp
= 0; /* EOF */
571 if (n
!= HAMMER_MREC_HEADSIZE
) {
572 fprintf(stderr
, "short read of mrecord header\n");
577 if (mrechd
.signature
!= HAMMER_IOC_MIRROR_SIGNATURE
) {
578 fprintf(stderr
, "read_mrecord: bad signature\n");
582 bytes
= mrechd
.rec_size
;
583 if (bytes
< HAMMER_MREC_HEADSIZE
)
584 bytes
= (int)HAMMER_MREC_HEADSIZE
;
585 mrec
= malloc(bytes
);
588 i
= read(fdin
, (char *)mrec
+ n
, bytes
- n
);
594 fprintf(stderr
, "read_mrecord: short read on payload\n");
598 if (mrec
->rec_crc
!= crc32((char *)mrec
+ HAMMER_MREC_CRCOFF
,
599 bytes
- HAMMER_MREC_CRCOFF
)) {
600 fprintf(stderr
, "read_mrecord: bad CRC\n");
604 mrec
->rec_size
-= HAMMER_MREC_HEADSIZE
;
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
;
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
));
632 * Generate a mirroring header with the pfs information of the
633 * originating filesytem.
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
));
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");
651 if (pfs
.version
!= HAMMER_IOC_PSEUDOFS_VERSION
) {
652 fprintf(stderr
, "mirror-read: HAMMER pfs version mismatch!\n");
657 * sync_beg_tid - lowest TID on source after which a full history
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
;
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.
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
;
688 * Get the PFSD info from the target filesystem.
690 bzero(&pfs
, sizeof(pfs
));
691 bzero(&pfsd
, sizeof(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");
699 if (pfs
.version
!= HAMMER_IOC_PSEUDOFS_VERSION
) {
700 fprintf(stderr
, "mirror-write: HAMMER pfs version mismatch!\n");
704 mrec
= read_mrecord(fdin
, &error
, NULL
);
707 fprintf(stderr
, "validate_mrec_header: short read\n");
710 if (mrec
->type
!= HAMMER_MREC_TYPE_PFSD
) {
711 fprintf(stderr
, "validate_mrec_header: did not get expected "
712 "PFSD record type\n");
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 "
722 if (pfs_head
->version
!= pfs
.version
) {
723 fprintf(stderr
, "validate_mrec_header: Version mismatch\n");
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");
735 (pfsd
.mirror_flags
& HAMMER_PFSD_SLAVE
) == 0) {
736 fprintf(stderr
, "mirror-write: target must be in slave mode\n");
740 *tid_begp
= pfs_head
->pfsd
.sync_beg_tid
;
742 *tid_endp
= pfs_head
->pfsd
.sync_end_tid
;
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
));
756 pfs
.bytes
= sizeof(pfsd
);
757 if (ioctl(fd
, HAMMERIOC_GET_PSEUDOFS
, &pfs
) != 0) {
758 perror("update_pfs_snapshot (read)");
761 pfsd
.sync_end_tid
= snapshot_tid
;
762 if (ioctl(fd
, HAMMERIOC_SET_PSEUDOFS
, &pfs
) != 0) {
763 perror("update_pfs_snapshot (rewrite)");
770 mirror_usage(int code
)
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"