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*9dc76cb1SMatthew Dillon * $DragonFly: src/sbin/hammer/cmd_mirror.c,v 1.9 2008/07/12 23:05:30 dillon Exp $ 35a7fbbf91SMatthew Dillon */ 36a7fbbf91SMatthew Dillon 37a7fbbf91SMatthew Dillon #include "hammer.h" 38a7fbbf91SMatthew Dillon 39a7fbbf91SMatthew Dillon #define SERIALBUF_SIZE (512 * 1024) 40a7fbbf91SMatthew Dillon 41a7fbbf91SMatthew Dillon static int read_mrecords(int fd, char *buf, u_int size, 4217dd83bcSMatthew Dillon hammer_ioc_mrecord_head_t pickup); 4317dd83bcSMatthew Dillon static hammer_ioc_mrecord_any_t read_mrecord(int fdin, int *errorp, 4417dd83bcSMatthew Dillon hammer_ioc_mrecord_head_t pickup); 4517dd83bcSMatthew Dillon static void write_mrecord(int fdout, u_int32_t type, 4617dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec, int bytes); 47d4e5b69bSMatthew Dillon static void generate_mrec_header(int fd, int fdout, int pfs_id, 4834ebae70SMatthew Dillon hammer_tid_t *tid_begp, hammer_tid_t *tid_endp); 49d4e5b69bSMatthew Dillon static void validate_mrec_header(int fd, int fdin, int is_target, int pfs_id, 5034ebae70SMatthew Dillon hammer_tid_t *tid_begp, hammer_tid_t *tid_endp); 51d4e5b69bSMatthew Dillon static void update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id); 52a7fbbf91SMatthew Dillon static void mirror_usage(int code); 53a7fbbf91SMatthew Dillon 5417dd83bcSMatthew Dillon /* 5517dd83bcSMatthew Dillon * Generate a mirroring data stream from the specific source over the 5617dd83bcSMatthew Dillon * entire key range, but restricted to the specified transaction range. 5717dd83bcSMatthew Dillon * 5817dd83bcSMatthew Dillon * The HAMMER VFS does most of the work, we add a few new mrecord 5917dd83bcSMatthew Dillon * types to negotiate the TID ranges and verify that the entire 6017dd83bcSMatthew Dillon * stream made it to the destination. 6117dd83bcSMatthew Dillon */ 62a7fbbf91SMatthew Dillon void 63a7fbbf91SMatthew Dillon hammer_cmd_mirror_read(char **av, int ac) 64a7fbbf91SMatthew Dillon { 65a7fbbf91SMatthew Dillon struct hammer_ioc_mirror_rw mirror; 66d4e5b69bSMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 6717dd83bcSMatthew Dillon union hammer_ioc_mrecord_any mrec_tmp; 6817dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 69243ca327SMatthew Dillon hammer_tid_t sync_tid; 70a7fbbf91SMatthew Dillon const char *filesystem; 71a7fbbf91SMatthew Dillon char *buf = malloc(SERIALBUF_SIZE); 72243ca327SMatthew Dillon int interrupted = 0; 73243ca327SMatthew Dillon int error; 74a7fbbf91SMatthew Dillon int fd; 75243ca327SMatthew Dillon int n; 76243ca327SMatthew Dillon time_t base_t = time(NULL); 77a7fbbf91SMatthew Dillon 78a7fbbf91SMatthew Dillon if (ac > 2) 79a7fbbf91SMatthew Dillon mirror_usage(1); 80a7fbbf91SMatthew Dillon filesystem = av[0]; 81a7fbbf91SMatthew Dillon 82a7fbbf91SMatthew Dillon bzero(&mirror, sizeof(mirror)); 83a7fbbf91SMatthew Dillon hammer_key_beg_init(&mirror.key_beg); 84a7fbbf91SMatthew Dillon hammer_key_end_init(&mirror.key_end); 85a7fbbf91SMatthew Dillon 86d4e5b69bSMatthew Dillon fd = getpfs(&pfs, filesystem); 87a7fbbf91SMatthew Dillon 88243ca327SMatthew Dillon /* 89d4e5b69bSMatthew Dillon * In 2-way mode the target will send us a PFS info packet 90d4e5b69bSMatthew Dillon * first. Use the target's current snapshot TID as our default 91d4e5b69bSMatthew Dillon * begin TID. 92243ca327SMatthew Dillon */ 93d4e5b69bSMatthew Dillon mirror.tid_beg = 0; 94d4e5b69bSMatthew Dillon if (TwoWayPipeOpt) 95d4e5b69bSMatthew Dillon validate_mrec_header(fd, 0, 0, pfs.pfs_id, 96d4e5b69bSMatthew Dillon NULL, &mirror.tid_beg); 97d4e5b69bSMatthew Dillon 98d4e5b69bSMatthew Dillon /* 99d4e5b69bSMatthew Dillon * Write out the PFS header, tid_beg will be updated if our PFS 100d4e5b69bSMatthew Dillon * has a larger begin sync. tid_end is set to the latest source 101d4e5b69bSMatthew Dillon * TID whos flush cycle has completed. 102d4e5b69bSMatthew Dillon */ 103d4e5b69bSMatthew Dillon generate_mrec_header(fd, 1, pfs.pfs_id, 104d4e5b69bSMatthew Dillon &mirror.tid_beg, &mirror.tid_end); 105d4e5b69bSMatthew Dillon 106d4e5b69bSMatthew Dillon /* 107d4e5b69bSMatthew Dillon * A cycle file overrides the beginning TID 108d4e5b69bSMatthew Dillon */ 109243ca327SMatthew Dillon hammer_get_cycle(&mirror.key_beg, &mirror.tid_beg); 11034ebae70SMatthew Dillon 11117dd83bcSMatthew Dillon if (ac == 2) 11217dd83bcSMatthew Dillon mirror.tid_beg = strtoull(av[1], NULL, 0); 11317dd83bcSMatthew Dillon 114*9dc76cb1SMatthew Dillon fprintf(stderr, "Mirror-read: Mirror from %016llx to %016llx\n", 115243ca327SMatthew Dillon mirror.tid_beg, mirror.tid_end); 116243ca327SMatthew Dillon if (mirror.key_beg.obj_id != (int64_t)HAMMER_MIN_OBJID) { 117*9dc76cb1SMatthew Dillon fprintf(stderr, "Mirror-read: Resuming at object %016llx\n", 118243ca327SMatthew Dillon mirror.key_beg.obj_id); 119243ca327SMatthew Dillon } 120243ca327SMatthew Dillon 121243ca327SMatthew Dillon /* 122*9dc76cb1SMatthew Dillon * Nothing to do if begin equals end. 123*9dc76cb1SMatthew Dillon */ 124*9dc76cb1SMatthew Dillon if (mirror.tid_beg == mirror.tid_end) { 125*9dc76cb1SMatthew Dillon fprintf(stderr, "Mirror-read: No work to do, stopping\n"); 126*9dc76cb1SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_TERM, 127*9dc76cb1SMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.sync)); 128*9dc76cb1SMatthew Dillon goto done; 129*9dc76cb1SMatthew Dillon } 130*9dc76cb1SMatthew Dillon 131*9dc76cb1SMatthew Dillon /* 132243ca327SMatthew Dillon * Write out bulk records 133243ca327SMatthew Dillon */ 134a7fbbf91SMatthew Dillon mirror.ubuf = buf; 135a7fbbf91SMatthew Dillon mirror.size = SERIALBUF_SIZE; 136a7fbbf91SMatthew Dillon 137a7fbbf91SMatthew Dillon do { 138a7fbbf91SMatthew Dillon mirror.count = 0; 139d4e5b69bSMatthew Dillon mirror.pfs_id = pfs.pfs_id; 140d4e5b69bSMatthew Dillon mirror.shared_uuid = pfs.ondisk->shared_uuid; 141a7fbbf91SMatthew Dillon if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) { 142a7fbbf91SMatthew Dillon fprintf(stderr, "Mirror-read %s failed: %s\n", 143a7fbbf91SMatthew Dillon filesystem, strerror(errno)); 144a7fbbf91SMatthew Dillon exit(1); 145a7fbbf91SMatthew Dillon } 1469c67b4d2SMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) { 1479c67b4d2SMatthew Dillon fprintf(stderr, 1489c67b4d2SMatthew Dillon "Mirror-read %s fatal error %d\n", 1499c67b4d2SMatthew Dillon filesystem, mirror.head.error); 1509c67b4d2SMatthew Dillon exit(1); 1519c67b4d2SMatthew Dillon } 152243ca327SMatthew Dillon if (mirror.count) { 153243ca327SMatthew Dillon n = write(1, mirror.ubuf, mirror.count); 154243ca327SMatthew Dillon if (n != mirror.count) { 155243ca327SMatthew Dillon fprintf(stderr, "Mirror-read %s failed: " 156243ca327SMatthew Dillon "short write\n", 157243ca327SMatthew Dillon filesystem); 158243ca327SMatthew Dillon exit(1); 159243ca327SMatthew Dillon } 160a7fbbf91SMatthew Dillon } 161a7fbbf91SMatthew Dillon mirror.key_beg = mirror.key_cur; 162243ca327SMatthew Dillon if (TimeoutOpt && 163243ca327SMatthew Dillon (unsigned)(time(NULL) - base_t) > (unsigned)TimeoutOpt) { 164243ca327SMatthew Dillon fprintf(stderr, 165243ca327SMatthew Dillon "Mirror-read %s interrupted by timer at" 166243ca327SMatthew Dillon " %016llx\n", 167243ca327SMatthew Dillon filesystem, 168243ca327SMatthew Dillon mirror.key_cur.obj_id); 169243ca327SMatthew Dillon interrupted = 1; 170243ca327SMatthew Dillon break; 171243ca327SMatthew Dillon } 172a7fbbf91SMatthew Dillon } while (mirror.count != 0); 173a7fbbf91SMatthew Dillon 174243ca327SMatthew Dillon /* 175243ca327SMatthew Dillon * Write out the termination sync record 176243ca327SMatthew Dillon */ 17717dd83bcSMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_SYNC, 17817dd83bcSMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.sync)); 17934ebae70SMatthew Dillon 180243ca327SMatthew Dillon /* 181243ca327SMatthew Dillon * If the -2 option was given (automatic when doing mirror-copy), 182243ca327SMatthew Dillon * a two-way pipe is assumed and we expect a response mrec from 183243ca327SMatthew Dillon * the target. 184243ca327SMatthew Dillon */ 185243ca327SMatthew Dillon if (TwoWayPipeOpt) { 186243ca327SMatthew Dillon mrec = read_mrecord(0, &error, NULL); 18717dd83bcSMatthew Dillon if (mrec == NULL || 18817dd83bcSMatthew Dillon mrec->head.type != HAMMER_MREC_TYPE_UPDATE || 18917dd83bcSMatthew Dillon mrec->head.rec_size != sizeof(mrec->update)) { 190243ca327SMatthew Dillon fprintf(stderr, "mirror_read: Did not get final " 191243ca327SMatthew Dillon "acknowledgement packet from target\n"); 192243ca327SMatthew Dillon exit(1); 193243ca327SMatthew Dillon } 194243ca327SMatthew Dillon if (interrupted) { 195243ca327SMatthew Dillon if (CyclePath) { 196243ca327SMatthew Dillon hammer_set_cycle(&mirror.key_cur, mirror.tid_beg); 197243ca327SMatthew Dillon fprintf(stderr, "Cyclefile %s updated for continuation\n", CyclePath); 198243ca327SMatthew Dillon } 199243ca327SMatthew Dillon } else { 20017dd83bcSMatthew Dillon sync_tid = mrec->update.tid; 201243ca327SMatthew Dillon if (CyclePath) { 202243ca327SMatthew Dillon hammer_key_beg_init(&mirror.key_beg); 203243ca327SMatthew Dillon hammer_set_cycle(&mirror.key_beg, sync_tid); 204243ca327SMatthew Dillon fprintf(stderr, "Cyclefile %s updated to 0x%016llx\n", 205243ca327SMatthew Dillon CyclePath, sync_tid); 206243ca327SMatthew Dillon } 207243ca327SMatthew Dillon } 208243ca327SMatthew Dillon } else if (CyclePath) { 209243ca327SMatthew Dillon /* NOTE! mirror.tid_beg cannot be updated */ 210243ca327SMatthew Dillon fprintf(stderr, "Warning: cycle file (-c option) cannot be " 211243ca327SMatthew Dillon "fully updated unless you use mirror-copy\n"); 212243ca327SMatthew Dillon hammer_set_cycle(&mirror.key_beg, mirror.tid_beg); 213243ca327SMatthew Dillon } 214*9dc76cb1SMatthew Dillon done: 215a7fbbf91SMatthew Dillon fprintf(stderr, "Mirror-read %s succeeded\n", filesystem); 216a7fbbf91SMatthew Dillon } 217a7fbbf91SMatthew Dillon 21817dd83bcSMatthew Dillon /* 21917dd83bcSMatthew Dillon * Pipe the mirroring data stream on stdin to the HAMMER VFS, adding 22017dd83bcSMatthew Dillon * some additional packet types to negotiate TID ranges and to verify 22117dd83bcSMatthew Dillon * completion. The HAMMER VFS does most of the work. 22217dd83bcSMatthew Dillon * 22317dd83bcSMatthew Dillon * It is important to note that the mirror.key_{beg,end} range must 22417dd83bcSMatthew Dillon * match the ranged used by the original. For now both sides use 22517dd83bcSMatthew Dillon * range the entire key space. 22617dd83bcSMatthew Dillon * 22717dd83bcSMatthew Dillon * It is even more important that the records in the stream conform 22817dd83bcSMatthew Dillon * to the TID range also supplied in the stream. The HAMMER VFS will 22917dd83bcSMatthew Dillon * use the REC, PASS, and SKIP record types to track the portions of 23017dd83bcSMatthew Dillon * the B-Tree being scanned in order to be able to proactively delete 23117dd83bcSMatthew Dillon * records on the target within those active areas that are not mentioned 23217dd83bcSMatthew Dillon * by the source. 23317dd83bcSMatthew Dillon * 23417dd83bcSMatthew Dillon * The mirror.key_cur field is used by the VFS to do this tracking. It 23517dd83bcSMatthew Dillon * must be initialized to key_beg but then is persistently updated by 23617dd83bcSMatthew Dillon * the HAMMER VFS on each successive ioctl() call. If you blow up this 23717dd83bcSMatthew Dillon * field you will blow up the mirror target, possibly to the point of 23817dd83bcSMatthew Dillon * deleting everything. As a safety measure the HAMMER VFS simply marks 23917dd83bcSMatthew Dillon * the records that the source has destroyed as deleted on the target, 24017dd83bcSMatthew Dillon * and normal pruning operations will deal with their final disposition 24117dd83bcSMatthew Dillon * at some later time. 24217dd83bcSMatthew Dillon */ 243a7fbbf91SMatthew Dillon void 244a7fbbf91SMatthew Dillon hammer_cmd_mirror_write(char **av, int ac) 245a7fbbf91SMatthew Dillon { 246a7fbbf91SMatthew Dillon struct hammer_ioc_mirror_rw mirror; 247a7fbbf91SMatthew Dillon const char *filesystem; 248a7fbbf91SMatthew Dillon char *buf = malloc(SERIALBUF_SIZE); 249d4e5b69bSMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 25017dd83bcSMatthew Dillon struct hammer_ioc_mrecord_head pickup; 251243ca327SMatthew Dillon struct hammer_ioc_synctid synctid; 25217dd83bcSMatthew Dillon union hammer_ioc_mrecord_any mrec_tmp; 25317dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 254243ca327SMatthew Dillon int error; 255243ca327SMatthew Dillon int fd; 256a7fbbf91SMatthew Dillon 257a7fbbf91SMatthew Dillon if (ac > 2) 258a7fbbf91SMatthew Dillon mirror_usage(1); 259a7fbbf91SMatthew Dillon filesystem = av[0]; 260a7fbbf91SMatthew Dillon 261a7fbbf91SMatthew Dillon bzero(&mirror, sizeof(mirror)); 262a7fbbf91SMatthew Dillon hammer_key_beg_init(&mirror.key_beg); 263a7fbbf91SMatthew Dillon hammer_key_end_init(&mirror.key_end); 26417dd83bcSMatthew Dillon mirror.key_end = mirror.key_beg; 265a7fbbf91SMatthew Dillon 266d4e5b69bSMatthew Dillon fd = getpfs(&pfs, filesystem); 267a7fbbf91SMatthew Dillon 268243ca327SMatthew Dillon /* 269d4e5b69bSMatthew Dillon * In two-way mode the target writes out a PFS packet first. 270d4e5b69bSMatthew Dillon * The source uses our tid_end as its tid_beg by default, 271d4e5b69bSMatthew Dillon * picking up where it left off. 272243ca327SMatthew Dillon */ 273d4e5b69bSMatthew Dillon mirror.tid_beg = 0; 274d4e5b69bSMatthew Dillon if (TwoWayPipeOpt) { 275d4e5b69bSMatthew Dillon generate_mrec_header(fd, 1, pfs.pfs_id, 276d4e5b69bSMatthew Dillon &mirror.tid_beg, &mirror.tid_end); 277d4e5b69bSMatthew Dillon } 278d4e5b69bSMatthew Dillon 279d4e5b69bSMatthew Dillon /* 28017dd83bcSMatthew Dillon * Read and process the PFS header. The source informs us of 28117dd83bcSMatthew Dillon * the TID range the stream represents. 282d4e5b69bSMatthew Dillon */ 283d4e5b69bSMatthew Dillon validate_mrec_header(fd, 0, 1, pfs.pfs_id, 284d4e5b69bSMatthew Dillon &mirror.tid_beg, &mirror.tid_end); 28534ebae70SMatthew Dillon 286a7fbbf91SMatthew Dillon mirror.ubuf = buf; 287a7fbbf91SMatthew Dillon mirror.size = SERIALBUF_SIZE; 288a7fbbf91SMatthew Dillon 289a7fbbf91SMatthew Dillon pickup.signature = 0; 290243ca327SMatthew Dillon pickup.type = 0; 291a7fbbf91SMatthew Dillon 292243ca327SMatthew Dillon /* 29317dd83bcSMatthew Dillon * Read and process bulk records (REC, PASS, and SKIP types). 29417dd83bcSMatthew Dillon * 29517dd83bcSMatthew Dillon * On your life, do NOT mess with mirror.key_cur or your mirror 29617dd83bcSMatthew Dillon * target may become history. 297243ca327SMatthew Dillon */ 298a7fbbf91SMatthew Dillon for (;;) { 299a7fbbf91SMatthew Dillon mirror.count = 0; 300d4e5b69bSMatthew Dillon mirror.pfs_id = pfs.pfs_id; 301d4e5b69bSMatthew Dillon mirror.shared_uuid = pfs.ondisk->shared_uuid; 302a7fbbf91SMatthew Dillon mirror.size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup); 303a7fbbf91SMatthew Dillon if (mirror.size <= 0) 304a7fbbf91SMatthew Dillon break; 305a7fbbf91SMatthew Dillon if (ioctl(fd, HAMMERIOC_MIRROR_WRITE, &mirror) < 0) { 306a7fbbf91SMatthew Dillon fprintf(stderr, "Mirror-write %s failed: %s\n", 307a7fbbf91SMatthew Dillon filesystem, strerror(errno)); 308a7fbbf91SMatthew Dillon exit(1); 309a7fbbf91SMatthew Dillon } 31017dd83bcSMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) { 31117dd83bcSMatthew Dillon fprintf(stderr, 31217dd83bcSMatthew Dillon "Mirror-write %s fatal error %d\n", 31317dd83bcSMatthew Dillon filesystem, mirror.head.error); 31417dd83bcSMatthew Dillon exit(1); 31517dd83bcSMatthew Dillon } 316243ca327SMatthew Dillon #if 0 317a7fbbf91SMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_INTR) { 318a7fbbf91SMatthew Dillon fprintf(stderr, 319a7fbbf91SMatthew Dillon "Mirror-write %s interrupted by timer at" 320243ca327SMatthew Dillon " %016llx\n", 321a7fbbf91SMatthew Dillon filesystem, 322243ca327SMatthew Dillon mirror.key_cur.obj_id); 323a7fbbf91SMatthew Dillon exit(0); 324a7fbbf91SMatthew Dillon } 325243ca327SMatthew Dillon #endif 326a7fbbf91SMatthew Dillon } 327243ca327SMatthew Dillon 328243ca327SMatthew Dillon /* 329243ca327SMatthew Dillon * Read and process the termination sync record. 330243ca327SMatthew Dillon */ 331243ca327SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 332*9dc76cb1SMatthew Dillon 333*9dc76cb1SMatthew Dillon if (mrec && mrec->head.type == HAMMER_MREC_TYPE_TERM) { 334*9dc76cb1SMatthew Dillon fprintf(stderr, "Mirror-write: No work to do, stopping\n"); 335*9dc76cb1SMatthew Dillon return; 336*9dc76cb1SMatthew Dillon } 337*9dc76cb1SMatthew Dillon 33817dd83bcSMatthew Dillon if (mrec == NULL || 33917dd83bcSMatthew Dillon mrec->head.type != HAMMER_MREC_TYPE_SYNC || 34017dd83bcSMatthew Dillon mrec->head.rec_size != sizeof(mrec->sync)) { 341243ca327SMatthew Dillon fprintf(stderr, "Mirror-write %s: Did not get termination " 34217dd83bcSMatthew Dillon "sync record, or rec_size is wrong rt=%d\n", 34317dd83bcSMatthew Dillon filesystem, mrec->head.type); 344243ca327SMatthew Dillon } 34517dd83bcSMatthew Dillon free(mrec); 34617dd83bcSMatthew Dillon mrec = NULL; 347243ca327SMatthew Dillon 348243ca327SMatthew Dillon /* 349243ca327SMatthew Dillon * Update the PFS info on the target so the user has visibility 350243ca327SMatthew Dillon * into the new snapshot. 351243ca327SMatthew Dillon */ 352d4e5b69bSMatthew Dillon update_pfs_snapshot(fd, mirror.tid_end, pfs.pfs_id); 353243ca327SMatthew Dillon 354243ca327SMatthew Dillon /* 355243ca327SMatthew Dillon * Sync the target filesystem 356243ca327SMatthew Dillon */ 357243ca327SMatthew Dillon bzero(&synctid, sizeof(synctid)); 358243ca327SMatthew Dillon synctid.op = HAMMER_SYNCTID_SYNC2; 359243ca327SMatthew Dillon ioctl(fd, HAMMERIOC_SYNCTID, &synctid); 360243ca327SMatthew Dillon 361243ca327SMatthew Dillon fprintf(stderr, "Mirror-write %s: succeeded\n", filesystem); 362243ca327SMatthew Dillon 363243ca327SMatthew Dillon /* 364243ca327SMatthew Dillon * Report back to the originator. 365243ca327SMatthew Dillon */ 366243ca327SMatthew Dillon if (TwoWayPipeOpt) { 36717dd83bcSMatthew Dillon mrec_tmp.update.tid = mirror.tid_end; 368243ca327SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_UPDATE, 36917dd83bcSMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.update)); 370243ca327SMatthew Dillon } else { 371243ca327SMatthew Dillon printf("Source can update synctid to 0x%016llx\n", 372243ca327SMatthew Dillon mirror.tid_end); 373243ca327SMatthew Dillon } 374243ca327SMatthew Dillon } 375243ca327SMatthew Dillon 376243ca327SMatthew Dillon void 377243ca327SMatthew Dillon hammer_cmd_mirror_dump(void) 378243ca327SMatthew Dillon { 379243ca327SMatthew Dillon char *buf = malloc(SERIALBUF_SIZE); 38017dd83bcSMatthew Dillon struct hammer_ioc_mrecord_head pickup; 38117dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 382243ca327SMatthew Dillon int error; 383243ca327SMatthew Dillon int size; 38417dd83bcSMatthew Dillon int offset; 38517dd83bcSMatthew Dillon int bytes; 386243ca327SMatthew Dillon 387243ca327SMatthew Dillon /* 388243ca327SMatthew Dillon * Read and process the PFS header 389243ca327SMatthew Dillon */ 390243ca327SMatthew Dillon pickup.signature = 0; 391243ca327SMatthew Dillon pickup.type = 0; 392243ca327SMatthew Dillon 393243ca327SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 394243ca327SMatthew Dillon 395243ca327SMatthew Dillon /* 396243ca327SMatthew Dillon * Read and process bulk records 397243ca327SMatthew Dillon */ 398243ca327SMatthew Dillon for (;;) { 399243ca327SMatthew Dillon size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup); 400243ca327SMatthew Dillon if (size <= 0) 401243ca327SMatthew Dillon break; 40217dd83bcSMatthew Dillon offset = 0; 40317dd83bcSMatthew Dillon while (offset < size) { 40417dd83bcSMatthew Dillon mrec = (void *)((char *)buf + offset); 40517dd83bcSMatthew Dillon bytes = HAMMER_HEAD_DOALIGN(mrec->head.rec_size); 40617dd83bcSMatthew Dillon if (offset + bytes > size) { 40717dd83bcSMatthew Dillon fprintf(stderr, "Misaligned record\n"); 40817dd83bcSMatthew Dillon exit(1); 40917dd83bcSMatthew Dillon } 41017dd83bcSMatthew Dillon 41117dd83bcSMatthew Dillon switch(mrec->head.type) { 41217dd83bcSMatthew Dillon case HAMMER_MREC_TYPE_REC: 413243ca327SMatthew Dillon printf("Record obj=%016llx key=%016llx " 414243ca327SMatthew Dillon "rt=%02x ot=%02x\n", 41517dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_id, 41617dd83bcSMatthew Dillon mrec->rec.leaf.base.key, 41717dd83bcSMatthew Dillon mrec->rec.leaf.base.rec_type, 41817dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_type); 419243ca327SMatthew Dillon printf(" tids %016llx:%016llx data=%d\n", 42017dd83bcSMatthew Dillon mrec->rec.leaf.base.create_tid, 42117dd83bcSMatthew Dillon mrec->rec.leaf.base.delete_tid, 42217dd83bcSMatthew Dillon mrec->rec.leaf.data_len); 42317dd83bcSMatthew Dillon break; 42417dd83bcSMatthew Dillon case HAMMER_MREC_TYPE_PASS: 42517dd83bcSMatthew Dillon printf("Pass obj=%016llx key=%016llx " 42617dd83bcSMatthew Dillon "rt=%02x ot=%02x\n", 42717dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_id, 42817dd83bcSMatthew Dillon mrec->rec.leaf.base.key, 42917dd83bcSMatthew Dillon mrec->rec.leaf.base.rec_type, 43017dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_type); 43117dd83bcSMatthew Dillon printf(" tids %016llx:%016llx data=%d\n", 43217dd83bcSMatthew Dillon mrec->rec.leaf.base.create_tid, 43317dd83bcSMatthew Dillon mrec->rec.leaf.base.delete_tid, 43417dd83bcSMatthew Dillon mrec->rec.leaf.data_len); 43517dd83bcSMatthew Dillon break; 43617dd83bcSMatthew Dillon case HAMMER_MREC_TYPE_SKIP: 43717dd83bcSMatthew Dillon printf("Skip obj=%016llx key=%016llx rt=%02x to\n" 43817dd83bcSMatthew Dillon " obj=%016llx key=%016llx rt=%02x\n", 43917dd83bcSMatthew Dillon mrec->skip.skip_beg.obj_id, 44017dd83bcSMatthew Dillon mrec->skip.skip_beg.key, 44117dd83bcSMatthew Dillon mrec->skip.skip_beg.rec_type, 44217dd83bcSMatthew Dillon mrec->skip.skip_end.obj_id, 44317dd83bcSMatthew Dillon mrec->skip.skip_end.key, 44417dd83bcSMatthew Dillon mrec->skip.skip_end.rec_type); 44517dd83bcSMatthew Dillon default: 44617dd83bcSMatthew Dillon break; 44717dd83bcSMatthew Dillon } 44817dd83bcSMatthew Dillon offset += bytes; 449243ca327SMatthew Dillon } 450243ca327SMatthew Dillon } 451243ca327SMatthew Dillon 452243ca327SMatthew Dillon /* 453243ca327SMatthew Dillon * Read and process the termination sync record. 454243ca327SMatthew Dillon */ 455243ca327SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 45617dd83bcSMatthew Dillon if (mrec == NULL || mrec->head.type != HAMMER_MREC_TYPE_SYNC) { 457243ca327SMatthew Dillon fprintf(stderr, "Mirror-dump: Did not get termination " 458243ca327SMatthew Dillon "sync record\n"); 459243ca327SMatthew Dillon } 460a7fbbf91SMatthew Dillon } 461a7fbbf91SMatthew Dillon 462a7fbbf91SMatthew Dillon void 463a7fbbf91SMatthew Dillon hammer_cmd_mirror_copy(char **av, int ac) 464a7fbbf91SMatthew Dillon { 46534ebae70SMatthew Dillon pid_t pid1; 46634ebae70SMatthew Dillon pid_t pid2; 46734ebae70SMatthew Dillon int fds[2]; 468243ca327SMatthew Dillon const char *xav[16]; 469243ca327SMatthew Dillon char tbuf[16]; 47034ebae70SMatthew Dillon char *ptr; 471243ca327SMatthew Dillon int xac; 47234ebae70SMatthew Dillon 47334ebae70SMatthew Dillon if (ac != 2) 47434ebae70SMatthew Dillon mirror_usage(1); 47534ebae70SMatthew Dillon 47634ebae70SMatthew Dillon if (pipe(fds) < 0) { 47734ebae70SMatthew Dillon perror("pipe"); 47834ebae70SMatthew Dillon exit(1); 47934ebae70SMatthew Dillon } 48034ebae70SMatthew Dillon 481243ca327SMatthew Dillon TwoWayPipeOpt = 1; 482243ca327SMatthew Dillon 48334ebae70SMatthew Dillon /* 48434ebae70SMatthew Dillon * Source 48534ebae70SMatthew Dillon */ 48634ebae70SMatthew Dillon if ((pid1 = fork()) == 0) { 48734ebae70SMatthew Dillon dup2(fds[0], 0); 48834ebae70SMatthew Dillon dup2(fds[0], 1); 48934ebae70SMatthew Dillon close(fds[0]); 49034ebae70SMatthew Dillon close(fds[1]); 49134ebae70SMatthew Dillon if ((ptr = strchr(av[0], ':')) != NULL) { 49234ebae70SMatthew Dillon *ptr++ = 0; 493243ca327SMatthew Dillon xac = 0; 494243ca327SMatthew Dillon xav[xac++] = "ssh"; 495243ca327SMatthew Dillon xav[xac++] = av[0]; 496243ca327SMatthew Dillon xav[xac++] = "hammer"; 497243ca327SMatthew Dillon if (VerboseOpt) 498243ca327SMatthew Dillon xav[xac++] = "-v"; 499243ca327SMatthew Dillon xav[xac++] = "-2"; 500243ca327SMatthew Dillon if (TimeoutOpt) { 501243ca327SMatthew Dillon snprintf(tbuf, sizeof(tbuf), "%d", TimeoutOpt); 502243ca327SMatthew Dillon xav[xac++] = "-t"; 503243ca327SMatthew Dillon xav[xac++] = tbuf; 504243ca327SMatthew Dillon } 505243ca327SMatthew Dillon xav[xac++] = "mirror-read"; 506243ca327SMatthew Dillon xav[xac++] = ptr; 507243ca327SMatthew Dillon xav[xac++] = NULL; 508243ca327SMatthew Dillon execv("/usr/bin/ssh", (void *)xav); 50934ebae70SMatthew Dillon } else { 51034ebae70SMatthew Dillon hammer_cmd_mirror_read(av, 1); 511243ca327SMatthew Dillon fflush(stdout); 512243ca327SMatthew Dillon fflush(stderr); 51334ebae70SMatthew Dillon } 51453d93cc7SMatthew Dillon _exit(1); 51534ebae70SMatthew Dillon } 51634ebae70SMatthew Dillon 51734ebae70SMatthew Dillon /* 51834ebae70SMatthew Dillon * Target 51934ebae70SMatthew Dillon */ 52034ebae70SMatthew Dillon if ((pid2 = fork()) == 0) { 52134ebae70SMatthew Dillon dup2(fds[1], 0); 52234ebae70SMatthew Dillon dup2(fds[1], 1); 52334ebae70SMatthew Dillon close(fds[0]); 52434ebae70SMatthew Dillon close(fds[1]); 52534ebae70SMatthew Dillon if ((ptr = strchr(av[1], ':')) != NULL) { 52634ebae70SMatthew Dillon *ptr++ = 0; 527243ca327SMatthew Dillon xac = 0; 528243ca327SMatthew Dillon xav[xac++] = "ssh"; 529243ca327SMatthew Dillon xav[xac++] = av[1]; 530243ca327SMatthew Dillon xav[xac++] = "hammer"; 531243ca327SMatthew Dillon if (VerboseOpt) 532243ca327SMatthew Dillon xav[xac++] = "-v"; 533243ca327SMatthew Dillon xav[xac++] = "-2"; 534243ca327SMatthew Dillon xav[xac++] = "mirror-write"; 535243ca327SMatthew Dillon xav[xac++] = ptr; 536243ca327SMatthew Dillon xav[xac++] = NULL; 537243ca327SMatthew Dillon execv("/usr/bin/ssh", (void *)xav); 53834ebae70SMatthew Dillon } else { 53934ebae70SMatthew Dillon hammer_cmd_mirror_write(av + 1, 1); 540243ca327SMatthew Dillon fflush(stdout); 541243ca327SMatthew Dillon fflush(stderr); 54234ebae70SMatthew Dillon } 54353d93cc7SMatthew Dillon _exit(1); 54434ebae70SMatthew Dillon } 54534ebae70SMatthew Dillon close(fds[0]); 54634ebae70SMatthew Dillon close(fds[1]); 54734ebae70SMatthew Dillon 54834ebae70SMatthew Dillon while (waitpid(pid1, NULL, 0) <= 0) 54934ebae70SMatthew Dillon ; 55034ebae70SMatthew Dillon while (waitpid(pid2, NULL, 0) <= 0) 55134ebae70SMatthew Dillon ; 552a7fbbf91SMatthew Dillon } 553a7fbbf91SMatthew Dillon 554243ca327SMatthew Dillon /* 555243ca327SMatthew Dillon * Read and return multiple mrecords 556243ca327SMatthew Dillon */ 557a7fbbf91SMatthew Dillon static int 55817dd83bcSMatthew Dillon read_mrecords(int fd, char *buf, u_int size, hammer_ioc_mrecord_head_t pickup) 559a7fbbf91SMatthew Dillon { 56017dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 561a7fbbf91SMatthew Dillon u_int count; 562a7fbbf91SMatthew Dillon size_t n; 563a7fbbf91SMatthew Dillon size_t i; 56417dd83bcSMatthew Dillon size_t bytes; 565a7fbbf91SMatthew Dillon 566a7fbbf91SMatthew Dillon count = 0; 567a7fbbf91SMatthew Dillon while (size - count >= HAMMER_MREC_HEADSIZE) { 568a7fbbf91SMatthew Dillon /* 569a7fbbf91SMatthew Dillon * Cached the record header in case we run out of buffer 570a7fbbf91SMatthew Dillon * space. 571a7fbbf91SMatthew Dillon */ 57217dd83bcSMatthew Dillon fflush(stdout); 573a7fbbf91SMatthew Dillon if (pickup->signature == 0) { 574a7fbbf91SMatthew Dillon for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) { 575a7fbbf91SMatthew Dillon i = read(fd, (char *)pickup + n, 576a7fbbf91SMatthew Dillon HAMMER_MREC_HEADSIZE - n); 577a7fbbf91SMatthew Dillon if (i <= 0) 578a7fbbf91SMatthew Dillon break; 579a7fbbf91SMatthew Dillon } 580a7fbbf91SMatthew Dillon if (n == 0) 581a7fbbf91SMatthew Dillon break; 582a7fbbf91SMatthew Dillon if (n != HAMMER_MREC_HEADSIZE) { 583a7fbbf91SMatthew Dillon fprintf(stderr, "read_mrecords: short read on pipe\n"); 584a7fbbf91SMatthew Dillon exit(1); 585a7fbbf91SMatthew Dillon } 586a7fbbf91SMatthew Dillon 587a7fbbf91SMatthew Dillon if (pickup->signature != HAMMER_IOC_MIRROR_SIGNATURE) { 588a7fbbf91SMatthew Dillon fprintf(stderr, "read_mrecords: malformed record on pipe, bad signature\n"); 589a7fbbf91SMatthew Dillon exit(1); 590a7fbbf91SMatthew Dillon } 591a7fbbf91SMatthew Dillon } 592a7fbbf91SMatthew Dillon if (pickup->rec_size < HAMMER_MREC_HEADSIZE || 59317dd83bcSMatthew Dillon pickup->rec_size > sizeof(*mrec) + HAMMER_XBUFSIZE) { 594a7fbbf91SMatthew Dillon fprintf(stderr, "read_mrecords: malformed record on pipe, illegal rec_size\n"); 595a7fbbf91SMatthew Dillon exit(1); 596a7fbbf91SMatthew Dillon } 597a7fbbf91SMatthew Dillon 598a7fbbf91SMatthew Dillon /* 599a7fbbf91SMatthew Dillon * Stop if we have insufficient space for the record and data. 600a7fbbf91SMatthew Dillon */ 60117dd83bcSMatthew Dillon bytes = HAMMER_HEAD_DOALIGN(pickup->rec_size); 60217dd83bcSMatthew Dillon if (size - count < bytes) 603a7fbbf91SMatthew Dillon break; 604a7fbbf91SMatthew Dillon 605a7fbbf91SMatthew Dillon /* 60617dd83bcSMatthew Dillon * Stop if the record type is not a REC or a SKIP (the only 60717dd83bcSMatthew Dillon * two types the ioctl supports. Other types are used only 60817dd83bcSMatthew Dillon * by the userland protocol). 609243ca327SMatthew Dillon */ 61017dd83bcSMatthew Dillon if (pickup->type != HAMMER_MREC_TYPE_REC && 61117dd83bcSMatthew Dillon pickup->type != HAMMER_MREC_TYPE_SKIP && 61217dd83bcSMatthew Dillon pickup->type != HAMMER_MREC_TYPE_PASS) { 613243ca327SMatthew Dillon break; 61417dd83bcSMatthew Dillon } 615243ca327SMatthew Dillon 616243ca327SMatthew Dillon /* 617a7fbbf91SMatthew Dillon * Read the remainder and clear the pickup signature. 618a7fbbf91SMatthew Dillon */ 61917dd83bcSMatthew Dillon for (n = HAMMER_MREC_HEADSIZE; n < bytes; n += i) { 62017dd83bcSMatthew Dillon i = read(fd, buf + count + n, bytes - n); 621a7fbbf91SMatthew Dillon if (i <= 0) 622a7fbbf91SMatthew Dillon break; 623a7fbbf91SMatthew Dillon } 62417dd83bcSMatthew Dillon if (n != bytes) { 625a7fbbf91SMatthew Dillon fprintf(stderr, "read_mrecords: short read on pipe\n"); 626a7fbbf91SMatthew Dillon exit(1); 627a7fbbf91SMatthew Dillon } 62817dd83bcSMatthew Dillon 62917dd83bcSMatthew Dillon bcopy(pickup, buf + count, HAMMER_MREC_HEADSIZE); 63017dd83bcSMatthew Dillon pickup->signature = 0; 63117dd83bcSMatthew Dillon pickup->type = 0; 63217dd83bcSMatthew Dillon mrec = (void *)(buf + count); 63317dd83bcSMatthew Dillon 63417dd83bcSMatthew Dillon /* 63517dd83bcSMatthew Dillon * Validate the completed record 63617dd83bcSMatthew Dillon */ 63717dd83bcSMatthew Dillon if (mrec->head.rec_crc != 63817dd83bcSMatthew Dillon crc32((char *)mrec + HAMMER_MREC_CRCOFF, 63917dd83bcSMatthew Dillon mrec->head.rec_size - HAMMER_MREC_CRCOFF)) { 64017dd83bcSMatthew Dillon fprintf(stderr, "read_mrecords: malformed record " 64117dd83bcSMatthew Dillon "on pipe, bad crc\n"); 64217dd83bcSMatthew Dillon exit(1); 643a7fbbf91SMatthew Dillon } 644a7fbbf91SMatthew Dillon 64517dd83bcSMatthew Dillon /* 64617dd83bcSMatthew Dillon * If its a B-Tree record validate the data crc 64717dd83bcSMatthew Dillon */ 64817dd83bcSMatthew Dillon if (mrec->head.type == HAMMER_MREC_TYPE_REC) { 64917dd83bcSMatthew Dillon if (mrec->head.rec_size < 65017dd83bcSMatthew Dillon sizeof(mrec->rec) + mrec->rec.leaf.data_len) { 65117dd83bcSMatthew Dillon fprintf(stderr, 65217dd83bcSMatthew Dillon "read_mrecords: malformed record on " 65317dd83bcSMatthew Dillon "pipe, illegal element data_len\n"); 65417dd83bcSMatthew Dillon exit(1); 65517dd83bcSMatthew Dillon } 65617dd83bcSMatthew Dillon if (mrec->rec.leaf.data_len && 65717dd83bcSMatthew Dillon mrec->rec.leaf.data_offset && 65817dd83bcSMatthew Dillon hammer_crc_test_leaf(&mrec->rec + 1, &mrec->rec.leaf) == 0) { 65917dd83bcSMatthew Dillon fprintf(stderr, 66017dd83bcSMatthew Dillon "read_mrecords: data_crc did not " 66117dd83bcSMatthew Dillon "match data! obj=%016llx key=%016llx\n", 66217dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_id, 66317dd83bcSMatthew Dillon mrec->rec.leaf.base.key); 66417dd83bcSMatthew Dillon fprintf(stderr, 66517dd83bcSMatthew Dillon "continuing, but there are problems\n"); 66617dd83bcSMatthew Dillon } 66717dd83bcSMatthew Dillon } 66817dd83bcSMatthew Dillon count += bytes; 669a7fbbf91SMatthew Dillon } 670a7fbbf91SMatthew Dillon return(count); 671a7fbbf91SMatthew Dillon } 672a7fbbf91SMatthew Dillon 67334ebae70SMatthew Dillon /* 67417dd83bcSMatthew Dillon * Read and return a single mrecord. 675243ca327SMatthew Dillon */ 676243ca327SMatthew Dillon static 67717dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t 67817dd83bcSMatthew Dillon read_mrecord(int fdin, int *errorp, hammer_ioc_mrecord_head_t pickup) 679243ca327SMatthew Dillon { 68017dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 68117dd83bcSMatthew Dillon struct hammer_ioc_mrecord_head mrechd; 682243ca327SMatthew Dillon size_t bytes; 683243ca327SMatthew Dillon size_t n; 684243ca327SMatthew Dillon size_t i; 685243ca327SMatthew Dillon 686243ca327SMatthew Dillon if (pickup && pickup->type != 0) { 687243ca327SMatthew Dillon mrechd = *pickup; 688243ca327SMatthew Dillon pickup->signature = 0; 689243ca327SMatthew Dillon pickup->type = 0; 690243ca327SMatthew Dillon n = HAMMER_MREC_HEADSIZE; 691243ca327SMatthew Dillon } else { 692243ca327SMatthew Dillon /* 693243ca327SMatthew Dillon * Read in the PFSD header from the sender. 694243ca327SMatthew Dillon */ 695243ca327SMatthew Dillon for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) { 696243ca327SMatthew Dillon i = read(fdin, (char *)&mrechd + n, HAMMER_MREC_HEADSIZE - n); 697243ca327SMatthew Dillon if (i <= 0) 698243ca327SMatthew Dillon break; 699243ca327SMatthew Dillon } 700243ca327SMatthew Dillon if (n == 0) { 701243ca327SMatthew Dillon *errorp = 0; /* EOF */ 702243ca327SMatthew Dillon return(NULL); 703243ca327SMatthew Dillon } 704243ca327SMatthew Dillon if (n != HAMMER_MREC_HEADSIZE) { 705243ca327SMatthew Dillon fprintf(stderr, "short read of mrecord header\n"); 706243ca327SMatthew Dillon *errorp = EPIPE; 707243ca327SMatthew Dillon return(NULL); 708243ca327SMatthew Dillon } 709243ca327SMatthew Dillon } 710243ca327SMatthew Dillon if (mrechd.signature != HAMMER_IOC_MIRROR_SIGNATURE) { 711243ca327SMatthew Dillon fprintf(stderr, "read_mrecord: bad signature\n"); 712243ca327SMatthew Dillon *errorp = EINVAL; 713243ca327SMatthew Dillon return(NULL); 714243ca327SMatthew Dillon } 71517dd83bcSMatthew Dillon bytes = HAMMER_HEAD_DOALIGN(mrechd.rec_size); 71617dd83bcSMatthew Dillon assert(bytes >= sizeof(mrechd)); 717243ca327SMatthew Dillon mrec = malloc(bytes); 71817dd83bcSMatthew Dillon mrec->head = mrechd; 71917dd83bcSMatthew Dillon 720243ca327SMatthew Dillon while (n < bytes) { 721243ca327SMatthew Dillon i = read(fdin, (char *)mrec + n, bytes - n); 722243ca327SMatthew Dillon if (i <= 0) 723243ca327SMatthew Dillon break; 724243ca327SMatthew Dillon n += i; 725243ca327SMatthew Dillon } 726243ca327SMatthew Dillon if (n != bytes) { 727243ca327SMatthew Dillon fprintf(stderr, "read_mrecord: short read on payload\n"); 728243ca327SMatthew Dillon *errorp = EPIPE; 729243ca327SMatthew Dillon return(NULL); 730243ca327SMatthew Dillon } 73117dd83bcSMatthew Dillon if (mrec->head.rec_crc != 73217dd83bcSMatthew Dillon crc32((char *)mrec + HAMMER_MREC_CRCOFF, 73317dd83bcSMatthew Dillon mrec->head.rec_size - HAMMER_MREC_CRCOFF)) { 734243ca327SMatthew Dillon fprintf(stderr, "read_mrecord: bad CRC\n"); 735243ca327SMatthew Dillon *errorp = EINVAL; 736243ca327SMatthew Dillon return(NULL); 737243ca327SMatthew Dillon } 738243ca327SMatthew Dillon *errorp = 0; 739243ca327SMatthew Dillon return(mrec); 740243ca327SMatthew Dillon } 741243ca327SMatthew Dillon 742243ca327SMatthew Dillon static 743243ca327SMatthew Dillon void 74417dd83bcSMatthew Dillon write_mrecord(int fdout, u_int32_t type, hammer_ioc_mrecord_any_t mrec, 74517dd83bcSMatthew Dillon int bytes) 746243ca327SMatthew Dillon { 74717dd83bcSMatthew Dillon char zbuf[HAMMER_HEAD_ALIGN]; 74817dd83bcSMatthew Dillon int pad; 749243ca327SMatthew Dillon 75017dd83bcSMatthew Dillon pad = HAMMER_HEAD_DOALIGN(bytes) - bytes; 75117dd83bcSMatthew Dillon 75217dd83bcSMatthew Dillon assert(bytes >= (int)sizeof(mrec->head)); 75317dd83bcSMatthew Dillon bzero(&mrec->head, sizeof(mrec->head)); 75417dd83bcSMatthew Dillon mrec->head.signature = HAMMER_IOC_MIRROR_SIGNATURE; 75517dd83bcSMatthew Dillon mrec->head.type = type; 75617dd83bcSMatthew Dillon mrec->head.rec_size = bytes; 75717dd83bcSMatthew Dillon mrec->head.rec_crc = crc32((char *)mrec + HAMMER_MREC_CRCOFF, 75817dd83bcSMatthew Dillon bytes - HAMMER_MREC_CRCOFF); 75917dd83bcSMatthew Dillon if (write(fdout, mrec, bytes) != bytes) { 760243ca327SMatthew Dillon fprintf(stderr, "write_mrecord: error %d (%s)\n", 761243ca327SMatthew Dillon errno, strerror(errno)); 762243ca327SMatthew Dillon exit(1); 763243ca327SMatthew Dillon } 76417dd83bcSMatthew Dillon if (pad) { 76517dd83bcSMatthew Dillon bzero(zbuf, pad); 76617dd83bcSMatthew Dillon if (write(fdout, zbuf, pad) != pad) { 76717dd83bcSMatthew Dillon fprintf(stderr, "write_mrecord: error %d (%s)\n", 76817dd83bcSMatthew Dillon errno, strerror(errno)); 76917dd83bcSMatthew Dillon exit(1); 77017dd83bcSMatthew Dillon } 77117dd83bcSMatthew Dillon } 772243ca327SMatthew Dillon } 773243ca327SMatthew Dillon 774243ca327SMatthew Dillon /* 77534ebae70SMatthew Dillon * Generate a mirroring header with the pfs information of the 77634ebae70SMatthew Dillon * originating filesytem. 77734ebae70SMatthew Dillon */ 77834ebae70SMatthew Dillon static void 779d4e5b69bSMatthew Dillon generate_mrec_header(int fd, int fdout, int pfs_id, 78034ebae70SMatthew Dillon hammer_tid_t *tid_begp, hammer_tid_t *tid_endp) 78134ebae70SMatthew Dillon { 78234ebae70SMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 78317dd83bcSMatthew Dillon union hammer_ioc_mrecord_any mrec_tmp; 78434ebae70SMatthew Dillon 78534ebae70SMatthew Dillon bzero(&pfs, sizeof(pfs)); 78617dd83bcSMatthew Dillon bzero(&mrec_tmp, sizeof(mrec_tmp)); 787d4e5b69bSMatthew Dillon pfs.pfs_id = pfs_id; 78817dd83bcSMatthew Dillon pfs.ondisk = &mrec_tmp.pfs.pfsd; 78917dd83bcSMatthew Dillon pfs.bytes = sizeof(mrec_tmp.pfs.pfsd); 79034ebae70SMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 791*9dc76cb1SMatthew Dillon fprintf(stderr, "Mirror-read: not a HAMMER fs/pseudofs!\n"); 79234ebae70SMatthew Dillon exit(1); 79334ebae70SMatthew Dillon } 79434ebae70SMatthew Dillon if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) { 795*9dc76cb1SMatthew Dillon fprintf(stderr, "Mirror-read: HAMMER pfs version mismatch!\n"); 79634ebae70SMatthew Dillon exit(1); 79734ebae70SMatthew Dillon } 79834ebae70SMatthew Dillon 79934ebae70SMatthew Dillon /* 80034ebae70SMatthew Dillon * sync_beg_tid - lowest TID on source after which a full history 80134ebae70SMatthew Dillon * is available. 80234ebae70SMatthew Dillon * 80334ebae70SMatthew Dillon * sync_end_tid - highest fully synchronized TID from source. 80434ebae70SMatthew Dillon */ 80517dd83bcSMatthew Dillon if (tid_begp && *tid_begp < mrec_tmp.pfs.pfsd.sync_beg_tid) 80617dd83bcSMatthew Dillon *tid_begp = mrec_tmp.pfs.pfsd.sync_beg_tid; 807d4e5b69bSMatthew Dillon if (tid_endp) 80817dd83bcSMatthew Dillon *tid_endp = mrec_tmp.pfs.pfsd.sync_end_tid; 80917dd83bcSMatthew Dillon mrec_tmp.pfs.version = pfs.version; 810243ca327SMatthew Dillon write_mrecord(fdout, HAMMER_MREC_TYPE_PFSD, 81117dd83bcSMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.pfs)); 81234ebae70SMatthew Dillon } 81334ebae70SMatthew Dillon 81434ebae70SMatthew Dillon /* 81534ebae70SMatthew Dillon * Validate the pfs information from the originating filesystem 81634ebae70SMatthew Dillon * against the target filesystem. shared_uuid must match. 81734ebae70SMatthew Dillon */ 81834ebae70SMatthew Dillon static void 819d4e5b69bSMatthew Dillon validate_mrec_header(int fd, int fdin, int is_target, int pfs_id, 82034ebae70SMatthew Dillon hammer_tid_t *tid_begp, hammer_tid_t *tid_endp) 82134ebae70SMatthew Dillon { 82234ebae70SMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 82334ebae70SMatthew Dillon struct hammer_pseudofs_data pfsd; 82417dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 825243ca327SMatthew Dillon int error; 82634ebae70SMatthew Dillon 82734ebae70SMatthew Dillon /* 82834ebae70SMatthew Dillon * Get the PFSD info from the target filesystem. 82934ebae70SMatthew Dillon */ 83034ebae70SMatthew Dillon bzero(&pfs, sizeof(pfs)); 83134ebae70SMatthew Dillon bzero(&pfsd, sizeof(pfsd)); 832d4e5b69bSMatthew Dillon pfs.pfs_id = pfs_id; 83334ebae70SMatthew Dillon pfs.ondisk = &pfsd; 83434ebae70SMatthew Dillon pfs.bytes = sizeof(pfsd); 83534ebae70SMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 83634ebae70SMatthew Dillon fprintf(stderr, "mirror-write: not a HAMMER fs/pseudofs!\n"); 83734ebae70SMatthew Dillon exit(1); 83834ebae70SMatthew Dillon } 83934ebae70SMatthew Dillon if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) { 84034ebae70SMatthew Dillon fprintf(stderr, "mirror-write: HAMMER pfs version mismatch!\n"); 84134ebae70SMatthew Dillon exit(1); 84234ebae70SMatthew Dillon } 84334ebae70SMatthew Dillon 844243ca327SMatthew Dillon mrec = read_mrecord(fdin, &error, NULL); 845243ca327SMatthew Dillon if (mrec == NULL) { 846243ca327SMatthew Dillon if (error == 0) 847243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: short read\n"); 84834ebae70SMatthew Dillon exit(1); 84934ebae70SMatthew Dillon } 85017dd83bcSMatthew Dillon if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) { 851243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: did not get expected " 852243ca327SMatthew Dillon "PFSD record type\n"); 85334ebae70SMatthew Dillon exit(1); 85434ebae70SMatthew Dillon } 85517dd83bcSMatthew Dillon if (mrec->head.rec_size != sizeof(mrec->pfs)) { 856243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: unexpected payload " 857243ca327SMatthew Dillon "size\n"); 85834ebae70SMatthew Dillon exit(1); 85934ebae70SMatthew Dillon } 86017dd83bcSMatthew Dillon if (mrec->pfs.version != pfs.version) { 861243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: Version mismatch\n"); 86234ebae70SMatthew Dillon exit(1); 86334ebae70SMatthew Dillon } 86434ebae70SMatthew Dillon 86534ebae70SMatthew Dillon /* 86634ebae70SMatthew Dillon * Whew. Ok, is the read PFS info compatible with the target? 86734ebae70SMatthew Dillon */ 86817dd83bcSMatthew Dillon if (bcmp(&mrec->pfs.pfsd.shared_uuid, &pfsd.shared_uuid, 86917dd83bcSMatthew Dillon sizeof(pfsd.shared_uuid)) != 0) { 87017dd83bcSMatthew Dillon fprintf(stderr, 87117dd83bcSMatthew Dillon "mirror-write: source and target have " 87217dd83bcSMatthew Dillon "different shared_uuid's!\n"); 87334ebae70SMatthew Dillon exit(1); 87434ebae70SMatthew Dillon } 875d4e5b69bSMatthew Dillon if (is_target && 876d4e5b69bSMatthew Dillon (pfsd.mirror_flags & HAMMER_PFSD_SLAVE) == 0) { 87734ebae70SMatthew Dillon fprintf(stderr, "mirror-write: target must be in slave mode\n"); 87834ebae70SMatthew Dillon exit(1); 87934ebae70SMatthew Dillon } 880d4e5b69bSMatthew Dillon if (tid_begp) 88117dd83bcSMatthew Dillon *tid_begp = mrec->pfs.pfsd.sync_beg_tid; 882d4e5b69bSMatthew Dillon if (tid_endp) 88317dd83bcSMatthew Dillon *tid_endp = mrec->pfs.pfsd.sync_end_tid; 884243ca327SMatthew Dillon free(mrec); 88534ebae70SMatthew Dillon } 88634ebae70SMatthew Dillon 88734ebae70SMatthew Dillon static void 888d4e5b69bSMatthew Dillon update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id) 88934ebae70SMatthew Dillon { 890243ca327SMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 891243ca327SMatthew Dillon struct hammer_pseudofs_data pfsd; 89234ebae70SMatthew Dillon 893243ca327SMatthew Dillon bzero(&pfs, sizeof(pfs)); 894243ca327SMatthew Dillon bzero(&pfsd, sizeof(pfsd)); 895d4e5b69bSMatthew Dillon pfs.pfs_id = pfs_id; 896243ca327SMatthew Dillon pfs.ondisk = &pfsd; 897243ca327SMatthew Dillon pfs.bytes = sizeof(pfsd); 898243ca327SMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 899243ca327SMatthew Dillon perror("update_pfs_snapshot (read)"); 900243ca327SMatthew Dillon exit(1); 90134ebae70SMatthew Dillon } 9029c67b4d2SMatthew Dillon if (pfsd.sync_end_tid != snapshot_tid) { 903ddc8e722SMatthew Dillon pfsd.sync_end_tid = snapshot_tid; 904243ca327SMatthew Dillon if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) != 0) { 905243ca327SMatthew Dillon perror("update_pfs_snapshot (rewrite)"); 906243ca327SMatthew Dillon exit(1); 90734ebae70SMatthew Dillon } 908*9dc76cb1SMatthew Dillon fprintf(stderr, 909*9dc76cb1SMatthew Dillon "Mirror-write: Completed, updated snapshot " 910*9dc76cb1SMatthew Dillon "to %016llx\n", 911*9dc76cb1SMatthew Dillon snapshot_tid); 912243ca327SMatthew Dillon } 9139c67b4d2SMatthew Dillon } 914243ca327SMatthew Dillon 91534ebae70SMatthew Dillon 916a7fbbf91SMatthew Dillon static void 917a7fbbf91SMatthew Dillon mirror_usage(int code) 918a7fbbf91SMatthew Dillon { 919a7fbbf91SMatthew Dillon fprintf(stderr, 920a7fbbf91SMatthew Dillon "hammer mirror-read <filesystem>\n" 921a7fbbf91SMatthew Dillon "hammer mirror-write <filesystem>\n" 922243ca327SMatthew Dillon "hammer mirror-dump\n" 923a7fbbf91SMatthew Dillon "hammer mirror-copy [[user@]host:]fs [[user@]host:]fs\n" 924a7fbbf91SMatthew Dillon ); 925a7fbbf91SMatthew Dillon exit(code); 926a7fbbf91SMatthew Dillon } 927