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.4 2008/07/07 00:27:22 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,
52 hammer_tid_t *tid_begp, hammer_tid_t *tid_endp);
53 static void validate_mrec_header(int fd, int fdin,
54 hammer_tid_t *tid_begp, hammer_tid_t *tid_endp);
55 static void update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid);
56 static void mirror_usage(int code);
59 hammer_cmd_mirror_read(char **av, int ac)
61 struct hammer_ioc_mirror_rw mirror;
62 hammer_ioc_mrecord_t mrec;
63 hammer_tid_t sync_tid;
64 const char *filesystem;
65 char *buf = malloc(SERIALBUF_SIZE);
70 time_t base_t = time(NULL);
76 bzero(&mirror, sizeof(mirror));
77 hammer_key_beg_init(&mirror.key_beg);
78 hammer_key_end_init(&mirror.key_end);
80 fd = open(filesystem, O_RDONLY);
82 err(1, "Unable to open %s", filesystem);
85 * Write out the PFS header
87 generate_mrec_header(fd, 1, &mirror.tid_beg, &mirror.tid_end);
88 hammer_get_cycle(&mirror.key_beg, &mirror.tid_beg);
90 fprintf(stderr, "mirror-read: Mirror from %016llx to %016llx\n",
91 mirror.tid_beg, mirror.tid_end);
92 if (mirror.key_beg.obj_id != (int64_t)HAMMER_MIN_OBJID) {
93 fprintf(stderr, "mirror-read: Resuming at object %016llx\n",
94 mirror.key_beg.obj_id);
98 * Write out bulk records
101 mirror.size = SERIALBUF_SIZE;
103 mirror.tid_beg = strtoull(av[1], NULL, 0);
107 if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) {
108 fprintf(stderr, "Mirror-read %s failed: %s\n",
109 filesystem, strerror(errno));
113 n = write(1, mirror.ubuf, mirror.count);
114 if (n != mirror.count) {
115 fprintf(stderr, "Mirror-read %s failed: "
121 mirror.key_beg = mirror.key_cur;
123 (unsigned)(time(NULL) - base_t) > (unsigned)TimeoutOpt) {
125 "Mirror-read %s interrupted by timer at"
128 mirror.key_cur.obj_id);
132 } while (mirror.count != 0);
135 * Write out the termination sync record
137 write_mrecord(1, HAMMER_MREC_TYPE_SYNC, NULL, 0);
140 * If the -2 option was given (automatic when doing mirror-copy),
141 * a two-way pipe is assumed and we expect a response mrec from
145 mrec = read_mrecord(0, &error, NULL);
146 if (mrec == NULL || mrec->type != HAMMER_MREC_TYPE_UPDATE) {
147 fprintf(stderr, "mirror_read: Did not get final "
148 "acknowledgement packet from target\n");
153 hammer_set_cycle(&mirror.key_cur, mirror.tid_beg);
154 fprintf(stderr, "Cyclefile %s updated for continuation\n", CyclePath);
157 sync_tid = *(hammer_tid_t *)(mrec + 1);
159 hammer_key_beg_init(&mirror.key_beg);
160 hammer_set_cycle(&mirror.key_beg, sync_tid);
161 fprintf(stderr, "Cyclefile %s updated to 0x%016llx\n",
162 CyclePath, sync_tid);
164 fprintf(stderr, "Source can update synctid "
169 } else if (CyclePath) {
170 /* NOTE! mirror.tid_beg cannot be updated */
171 fprintf(stderr, "Warning: cycle file (-c option) cannot be "
172 "fully updated unless you use mirror-copy\n");
173 hammer_set_cycle(&mirror.key_beg, mirror.tid_beg);
175 fprintf(stderr, "Mirror-read %s succeeded\n", filesystem);
179 hammer_cmd_mirror_write(char **av, int ac)
181 struct hammer_ioc_mirror_rw mirror;
182 const char *filesystem;
183 char *buf = malloc(SERIALBUF_SIZE);
184 struct hammer_ioc_mrecord pickup;
185 struct hammer_ioc_synctid synctid;
186 hammer_ioc_mrecord_t mrec;
194 bzero(&mirror, sizeof(mirror));
195 hammer_key_beg_init(&mirror.key_beg);
196 hammer_key_end_init(&mirror.key_end);
198 fd = open(filesystem, O_RDONLY);
200 err(1, "Unable to open %s", filesystem);
203 * Read and process the PFS header
205 validate_mrec_header(fd, 0, &mirror.tid_beg, &mirror.tid_end);
208 mirror.size = SERIALBUF_SIZE;
210 pickup.signature = 0;
214 * Read and process bulk records
218 mirror.size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup);
219 if (mirror.size <= 0)
221 if (ioctl(fd, HAMMERIOC_MIRROR_WRITE, &mirror) < 0) {
222 fprintf(stderr, "Mirror-write %s failed: %s\n",
223 filesystem, strerror(errno));
227 if (mirror.head.flags & HAMMER_IOC_HEAD_INTR) {
229 "Mirror-write %s interrupted by timer at"
232 mirror.key_cur.obj_id);
236 mirror.key_beg = mirror.key_cur;
240 * Read and process the termination sync record.
242 mrec = read_mrecord(0, &error, &pickup);
243 if (mrec == NULL || mrec->type != HAMMER_MREC_TYPE_SYNC) {
244 fprintf(stderr, "Mirror-write %s: Did not get termination "
250 * Update the PFS info on the target so the user has visibility
251 * into the new snapshot.
253 update_pfs_snapshot(fd, mirror.tid_end);
256 * Sync the target filesystem
258 bzero(&synctid, sizeof(synctid));
259 synctid.op = HAMMER_SYNCTID_SYNC2;
260 ioctl(fd, HAMMERIOC_SYNCTID, &synctid);
262 fprintf(stderr, "Mirror-write %s: succeeded\n", filesystem);
265 * Report back to the originator.
268 write_mrecord(1, HAMMER_MREC_TYPE_UPDATE,
269 &mirror.tid_end, sizeof(mirror.tid_end));
271 printf("Source can update synctid to 0x%016llx\n",
277 hammer_cmd_mirror_dump(void)
279 char *buf = malloc(SERIALBUF_SIZE);
280 struct hammer_ioc_mrecord pickup;
281 hammer_ioc_mrecord_t mrec;
286 * Read and process the PFS header
288 pickup.signature = 0;
291 mrec = read_mrecord(0, &error, &pickup);
294 * Read and process bulk records
297 size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup);
301 while (mrec < (hammer_ioc_mrecord_t)((char *)buf + size)) {
302 printf("Record obj=%016llx key=%016llx "
304 mrec->leaf.base.obj_id,
306 mrec->leaf.base.rec_type,
307 mrec->leaf.base.obj_type);
308 printf(" tids %016llx:%016llx data=%d\n",
309 mrec->leaf.base.create_tid,
310 mrec->leaf.base.delete_tid,
311 mrec->leaf.data_len);
312 mrec = (void *)((char *)mrec + mrec->rec_size);
317 * Read and process the termination sync record.
319 mrec = read_mrecord(0, &error, &pickup);
320 if (mrec == NULL || mrec->type != HAMMER_MREC_TYPE_SYNC) {
321 fprintf(stderr, "Mirror-dump: Did not get termination "
327 hammer_cmd_mirror_copy(char **av, int ac)
350 if ((pid1 = fork()) == 0) {
355 if ((ptr = strchr(av[0], ':')) != NULL) {
360 xav[xac++] = "hammer";
365 snprintf(tbuf, sizeof(tbuf), "%d", TimeoutOpt);
369 xav[xac++] = "mirror-read";
372 execv("/usr/bin/ssh", (void *)xav);
374 hammer_cmd_mirror_read(av, 1);
384 if ((pid2 = fork()) == 0) {
389 if ((ptr = strchr(av[1], ':')) != NULL) {
394 xav[xac++] = "hammer";
398 xav[xac++] = "mirror-write";
401 execv("/usr/bin/ssh", (void *)xav);
403 hammer_cmd_mirror_write(av + 1, 1);
412 while (waitpid(pid1, NULL, 0) <= 0)
414 while (waitpid(pid2, NULL, 0) <= 0)
419 * Read and return multiple mrecords
422 read_mrecords(int fd, char *buf, u_int size, hammer_ioc_mrecord_t pickup)
429 while (size - count >= HAMMER_MREC_HEADSIZE) {
431 * Cached the record header in case we run out of buffer
434 if (pickup->signature == 0) {
435 for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) {
436 i = read(fd, (char *)pickup + n,
437 HAMMER_MREC_HEADSIZE - n);
443 if (n != HAMMER_MREC_HEADSIZE) {
444 fprintf(stderr, "read_mrecords: short read on pipe\n");
448 if (pickup->signature != HAMMER_IOC_MIRROR_SIGNATURE) {
449 fprintf(stderr, "read_mrecords: malformed record on pipe, bad signature\n");
452 if (pickup->rec_crc != crc32((char *)pickup + HAMMER_MREC_CRCOFF, HAMMER_MREC_HEADSIZE - HAMMER_MREC_CRCOFF)) {
453 fprintf(stderr, "read_mrecords: malformed record on pipe, bad crc\n");
457 if (pickup->rec_size < HAMMER_MREC_HEADSIZE ||
458 pickup->rec_size > HAMMER_MREC_HEADSIZE + HAMMER_XBUFSIZE) {
459 fprintf(stderr, "read_mrecords: malformed record on pipe, illegal rec_size\n");
462 if (HAMMER_MREC_HEADSIZE + pickup->leaf.data_len > pickup->rec_size) {
463 fprintf(stderr, "read_mrecords: malformed record on pipe, illegal element data_len\n");
468 * Stop if we have insufficient space for the record and data.
470 if (size - count < pickup->rec_size)
474 * Stop if the record type is not HAMMER_MREC_TYPE_REC
476 if (pickup->type != HAMMER_MREC_TYPE_REC)
480 * Read the remainder and clear the pickup signature.
482 bcopy(pickup, buf + count, HAMMER_MREC_HEADSIZE);
483 pickup->signature = 0;
485 for (n = HAMMER_MREC_HEADSIZE; n < pickup->rec_size; n += i) {
486 i = read(fd, buf + count + n, pickup->rec_size - n);
490 if (n != pickup->rec_size) {
491 fprintf(stderr, "read_mrecords: short read on pipe\n");
494 if (pickup->leaf.data_len && pickup->leaf.data_offset) {
495 if (hammer_crc_test_leaf(buf + count + HAMMER_MREC_HEADSIZE, &pickup->leaf) == 0) {
496 fprintf(stderr, "read_mrecords: data_crc did not match data! obj=%016llx key=%016llx\n", pickup->leaf.base.obj_id, pickup->leaf.base.key);
497 fprintf(stderr, "continuing, but there are problems\n");
501 count += pickup->rec_size;
507 * Read and return a single mrecord. The returned mrec->rec_size will be
508 * adjusted to be the size of the payload.
511 struct hammer_ioc_mrecord *
512 read_mrecord(int fdin, int *errorp, hammer_ioc_mrecord_t pickup)
514 hammer_ioc_mrecord_t mrec;
515 struct hammer_ioc_mrecord mrechd;
520 if (pickup && pickup->type != 0) {
522 pickup->signature = 0;
524 n = HAMMER_MREC_HEADSIZE;
527 * Read in the PFSD header from the sender.
529 for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) {
530 i = read(fdin, (char *)&mrechd + n, HAMMER_MREC_HEADSIZE - n);
535 *errorp = 0; /* EOF */
538 if (n != HAMMER_MREC_HEADSIZE) {
539 fprintf(stderr, "short read of mrecord header\n");
544 if (mrechd.signature != HAMMER_IOC_MIRROR_SIGNATURE) {
545 fprintf(stderr, "read_mrecord: bad signature\n");
549 bytes = mrechd.rec_size;
550 if (bytes < HAMMER_MREC_HEADSIZE)
551 bytes = (int)HAMMER_MREC_HEADSIZE;
552 mrec = malloc(bytes);
555 i = read(fdin, (char *)mrec + n, bytes - n);
561 fprintf(stderr, "read_mrecord: short read on payload\n");
565 if (mrec->rec_crc != crc32((char *)mrec + HAMMER_MREC_CRCOFF,
566 bytes - HAMMER_MREC_CRCOFF)) {
567 fprintf(stderr, "read_mrecord: bad CRC\n");
571 mrec->rec_size -= HAMMER_MREC_HEADSIZE;
578 write_mrecord(int fdout, u_int32_t type, void *payload, int bytes)
580 hammer_ioc_mrecord_t mrec;
582 mrec = malloc(HAMMER_MREC_HEADSIZE + bytes);
583 bzero(mrec, sizeof(*mrec));
584 mrec->signature = HAMMER_IOC_MIRROR_SIGNATURE;
586 mrec->rec_size = HAMMER_MREC_HEADSIZE + bytes;
587 bcopy(payload, mrec + 1, bytes);
588 mrec->rec_crc = crc32((char *)mrec + HAMMER_MREC_CRCOFF,
589 mrec->rec_size - HAMMER_MREC_CRCOFF);
590 if (write(fdout, mrec, mrec->rec_size) != (int)mrec->rec_size) {
591 fprintf(stderr, "write_mrecord: error %d (%s)\n",
592 errno, strerror(errno));
599 * Generate a mirroring header with the pfs information of the
600 * originating filesytem.
603 generate_mrec_header(int fd, int fdout,
604 hammer_tid_t *tid_begp, hammer_tid_t *tid_endp)
606 struct hammer_ioc_pseudofs_rw pfs;
607 struct hammer_pfs_head pfs_head;
609 bzero(&pfs, sizeof(pfs));
610 bzero(&pfs_head, sizeof(pfs_head));
611 pfs.ondisk = &pfs_head.pfsd;
612 pfs.bytes = sizeof(pfs_head.pfsd);
613 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
614 fprintf(stderr, "mirror-read: not a HAMMER fs/pseudofs!\n");
617 if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
618 fprintf(stderr, "mirror-read: HAMMER pfs version mismatch!\n");
623 * sync_beg_tid - lowest TID on source after which a full history
626 * sync_end_tid - highest fully synchronized TID from source.
628 *tid_begp = pfs_head.pfsd.sync_beg_tid;
629 *tid_endp = pfs_head.pfsd.sync_end_tid;
631 pfs_head.version = pfs.version;
632 write_mrecord(fdout, HAMMER_MREC_TYPE_PFSD,
633 &pfs_head, sizeof(pfs_head));
637 * Validate the pfs information from the originating filesystem
638 * against the target filesystem. shared_uuid must match.
641 validate_mrec_header(int fd, int fdin,
642 hammer_tid_t *tid_begp, hammer_tid_t *tid_endp)
644 struct hammer_ioc_pseudofs_rw pfs;
645 struct hammer_pfs_head *pfs_head;
646 struct hammer_pseudofs_data pfsd;
647 hammer_ioc_mrecord_t mrec;
652 * Get the PFSD info from the target filesystem.
654 bzero(&pfs, sizeof(pfs));
655 bzero(&pfsd, sizeof(pfsd));
657 pfs.bytes = sizeof(pfsd);
658 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
659 fprintf(stderr, "mirror-write: not a HAMMER fs/pseudofs!\n");
662 if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
663 fprintf(stderr, "mirror-write: HAMMER pfs version mismatch!\n");
667 mrec = read_mrecord(fdin, &error, NULL);
670 fprintf(stderr, "validate_mrec_header: short read\n");
673 if (mrec->type != HAMMER_MREC_TYPE_PFSD) {
674 fprintf(stderr, "validate_mrec_header: did not get expected "
675 "PFSD record type\n");
678 pfs_head = (void *)(mrec + 1);
679 bytes = mrec->rec_size; /* post-adjusted for payload */
680 if (bytes != sizeof(*pfs_head)) {
681 fprintf(stderr, "validate_mrec_header: unexpected payload "
685 if (pfs_head->version != pfs.version) {
686 fprintf(stderr, "validate_mrec_header: Version mismatch\n");
691 * Whew. Ok, is the read PFS info compatible with the target?
693 if (bcmp(&pfs_head->pfsd.shared_uuid, &pfsd.shared_uuid, sizeof(pfsd.shared_uuid)) != 0) {
694 fprintf(stderr, "mirror-write: source and target have different shared_uuid's!\n");
697 if ((pfsd.mirror_flags & HAMMER_PFSD_SLAVE) == 0) {
698 fprintf(stderr, "mirror-write: target must be in slave mode\n");
701 *tid_begp = pfs_head->pfsd.sync_beg_tid;
702 *tid_endp = pfs_head->pfsd.sync_end_tid;
707 update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid)
709 struct hammer_ioc_pseudofs_rw pfs;
710 struct hammer_pseudofs_data pfsd;
712 bzero(&pfs, sizeof(pfs));
713 bzero(&pfsd, sizeof(pfsd));
715 pfs.bytes = sizeof(pfsd);
716 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
717 perror("update_pfs_snapshot (read)");
720 pfsd.sync_beg_tid = snapshot_tid;
721 if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) != 0) {
722 perror("update_pfs_snapshot (rewrite)");
729 mirror_usage(int code)
732 "hammer mirror-read <filesystem>\n"
733 "hammer mirror-write <filesystem>\n"
734 "hammer mirror-dump\n"
735 "hammer mirror-copy [[user@]host:]fs [[user@]host:]fs\n"