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*9c67b4d2SMatthew Dillon * $DragonFly: src/sbin/hammer/cmd_mirror.c,v 1.8 2008/07/12 02:48:46 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 114243ca327SMatthew 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) { 117243ca327SMatthew Dillon fprintf(stderr, "mirror-read: Resuming at object %016llx\n", 118243ca327SMatthew Dillon mirror.key_beg.obj_id); 119243ca327SMatthew Dillon } 120243ca327SMatthew Dillon 121243ca327SMatthew Dillon /* 122243ca327SMatthew Dillon * Write out bulk records 123243ca327SMatthew Dillon */ 124a7fbbf91SMatthew Dillon mirror.ubuf = buf; 125a7fbbf91SMatthew Dillon mirror.size = SERIALBUF_SIZE; 126a7fbbf91SMatthew Dillon 127a7fbbf91SMatthew Dillon do { 128a7fbbf91SMatthew Dillon mirror.count = 0; 129d4e5b69bSMatthew Dillon mirror.pfs_id = pfs.pfs_id; 130d4e5b69bSMatthew Dillon mirror.shared_uuid = pfs.ondisk->shared_uuid; 131a7fbbf91SMatthew Dillon if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) { 132a7fbbf91SMatthew Dillon fprintf(stderr, "Mirror-read %s failed: %s\n", 133a7fbbf91SMatthew Dillon filesystem, strerror(errno)); 134a7fbbf91SMatthew Dillon exit(1); 135a7fbbf91SMatthew Dillon } 136*9c67b4d2SMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) { 137*9c67b4d2SMatthew Dillon fprintf(stderr, 138*9c67b4d2SMatthew Dillon "Mirror-read %s fatal error %d\n", 139*9c67b4d2SMatthew Dillon filesystem, mirror.head.error); 140*9c67b4d2SMatthew Dillon exit(1); 141*9c67b4d2SMatthew Dillon } 142243ca327SMatthew Dillon if (mirror.count) { 143243ca327SMatthew Dillon n = write(1, mirror.ubuf, mirror.count); 144243ca327SMatthew Dillon if (n != mirror.count) { 145243ca327SMatthew Dillon fprintf(stderr, "Mirror-read %s failed: " 146243ca327SMatthew Dillon "short write\n", 147243ca327SMatthew Dillon filesystem); 148243ca327SMatthew Dillon exit(1); 149243ca327SMatthew Dillon } 150a7fbbf91SMatthew Dillon } 151a7fbbf91SMatthew Dillon mirror.key_beg = mirror.key_cur; 152243ca327SMatthew Dillon if (TimeoutOpt && 153243ca327SMatthew Dillon (unsigned)(time(NULL) - base_t) > (unsigned)TimeoutOpt) { 154243ca327SMatthew Dillon fprintf(stderr, 155243ca327SMatthew Dillon "Mirror-read %s interrupted by timer at" 156243ca327SMatthew Dillon " %016llx\n", 157243ca327SMatthew Dillon filesystem, 158243ca327SMatthew Dillon mirror.key_cur.obj_id); 159243ca327SMatthew Dillon interrupted = 1; 160243ca327SMatthew Dillon break; 161243ca327SMatthew Dillon } 162a7fbbf91SMatthew Dillon } while (mirror.count != 0); 163a7fbbf91SMatthew Dillon 164243ca327SMatthew Dillon /* 165243ca327SMatthew Dillon * Write out the termination sync record 166243ca327SMatthew Dillon */ 16717dd83bcSMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_SYNC, 16817dd83bcSMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.sync)); 16934ebae70SMatthew Dillon 170243ca327SMatthew Dillon /* 171243ca327SMatthew Dillon * If the -2 option was given (automatic when doing mirror-copy), 172243ca327SMatthew Dillon * a two-way pipe is assumed and we expect a response mrec from 173243ca327SMatthew Dillon * the target. 174243ca327SMatthew Dillon */ 175243ca327SMatthew Dillon if (TwoWayPipeOpt) { 176243ca327SMatthew Dillon mrec = read_mrecord(0, &error, NULL); 17717dd83bcSMatthew Dillon if (mrec == NULL || 17817dd83bcSMatthew Dillon mrec->head.type != HAMMER_MREC_TYPE_UPDATE || 17917dd83bcSMatthew Dillon mrec->head.rec_size != sizeof(mrec->update)) { 180243ca327SMatthew Dillon fprintf(stderr, "mirror_read: Did not get final " 181243ca327SMatthew Dillon "acknowledgement packet from target\n"); 182243ca327SMatthew Dillon exit(1); 183243ca327SMatthew Dillon } 184243ca327SMatthew Dillon if (interrupted) { 185243ca327SMatthew Dillon if (CyclePath) { 186243ca327SMatthew Dillon hammer_set_cycle(&mirror.key_cur, mirror.tid_beg); 187243ca327SMatthew Dillon fprintf(stderr, "Cyclefile %s updated for continuation\n", CyclePath); 188243ca327SMatthew Dillon } 189243ca327SMatthew Dillon } else { 19017dd83bcSMatthew Dillon sync_tid = mrec->update.tid; 191243ca327SMatthew Dillon if (CyclePath) { 192243ca327SMatthew Dillon hammer_key_beg_init(&mirror.key_beg); 193243ca327SMatthew Dillon hammer_set_cycle(&mirror.key_beg, sync_tid); 194243ca327SMatthew Dillon fprintf(stderr, "Cyclefile %s updated to 0x%016llx\n", 195243ca327SMatthew Dillon CyclePath, sync_tid); 196243ca327SMatthew Dillon } else { 197243ca327SMatthew Dillon fprintf(stderr, "Source can update synctid " 198243ca327SMatthew Dillon "to 0x%016llx\n", 199243ca327SMatthew Dillon sync_tid); 200243ca327SMatthew Dillon } 201243ca327SMatthew Dillon } 202243ca327SMatthew Dillon } else if (CyclePath) { 203243ca327SMatthew Dillon /* NOTE! mirror.tid_beg cannot be updated */ 204243ca327SMatthew Dillon fprintf(stderr, "Warning: cycle file (-c option) cannot be " 205243ca327SMatthew Dillon "fully updated unless you use mirror-copy\n"); 206243ca327SMatthew Dillon hammer_set_cycle(&mirror.key_beg, mirror.tid_beg); 207243ca327SMatthew Dillon } 208a7fbbf91SMatthew Dillon fprintf(stderr, "Mirror-read %s succeeded\n", filesystem); 209a7fbbf91SMatthew Dillon } 210a7fbbf91SMatthew Dillon 21117dd83bcSMatthew Dillon /* 21217dd83bcSMatthew Dillon * Pipe the mirroring data stream on stdin to the HAMMER VFS, adding 21317dd83bcSMatthew Dillon * some additional packet types to negotiate TID ranges and to verify 21417dd83bcSMatthew Dillon * completion. The HAMMER VFS does most of the work. 21517dd83bcSMatthew Dillon * 21617dd83bcSMatthew Dillon * It is important to note that the mirror.key_{beg,end} range must 21717dd83bcSMatthew Dillon * match the ranged used by the original. For now both sides use 21817dd83bcSMatthew Dillon * range the entire key space. 21917dd83bcSMatthew Dillon * 22017dd83bcSMatthew Dillon * It is even more important that the records in the stream conform 22117dd83bcSMatthew Dillon * to the TID range also supplied in the stream. The HAMMER VFS will 22217dd83bcSMatthew Dillon * use the REC, PASS, and SKIP record types to track the portions of 22317dd83bcSMatthew Dillon * the B-Tree being scanned in order to be able to proactively delete 22417dd83bcSMatthew Dillon * records on the target within those active areas that are not mentioned 22517dd83bcSMatthew Dillon * by the source. 22617dd83bcSMatthew Dillon * 22717dd83bcSMatthew Dillon * The mirror.key_cur field is used by the VFS to do this tracking. It 22817dd83bcSMatthew Dillon * must be initialized to key_beg but then is persistently updated by 22917dd83bcSMatthew Dillon * the HAMMER VFS on each successive ioctl() call. If you blow up this 23017dd83bcSMatthew Dillon * field you will blow up the mirror target, possibly to the point of 23117dd83bcSMatthew Dillon * deleting everything. As a safety measure the HAMMER VFS simply marks 23217dd83bcSMatthew Dillon * the records that the source has destroyed as deleted on the target, 23317dd83bcSMatthew Dillon * and normal pruning operations will deal with their final disposition 23417dd83bcSMatthew Dillon * at some later time. 23517dd83bcSMatthew Dillon */ 236a7fbbf91SMatthew Dillon void 237a7fbbf91SMatthew Dillon hammer_cmd_mirror_write(char **av, int ac) 238a7fbbf91SMatthew Dillon { 239a7fbbf91SMatthew Dillon struct hammer_ioc_mirror_rw mirror; 240a7fbbf91SMatthew Dillon const char *filesystem; 241a7fbbf91SMatthew Dillon char *buf = malloc(SERIALBUF_SIZE); 242d4e5b69bSMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 24317dd83bcSMatthew Dillon struct hammer_ioc_mrecord_head pickup; 244243ca327SMatthew Dillon struct hammer_ioc_synctid synctid; 24517dd83bcSMatthew Dillon union hammer_ioc_mrecord_any mrec_tmp; 24617dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 247243ca327SMatthew Dillon int error; 248243ca327SMatthew Dillon int fd; 249a7fbbf91SMatthew Dillon 250a7fbbf91SMatthew Dillon if (ac > 2) 251a7fbbf91SMatthew Dillon mirror_usage(1); 252a7fbbf91SMatthew Dillon filesystem = av[0]; 253a7fbbf91SMatthew Dillon 254a7fbbf91SMatthew Dillon bzero(&mirror, sizeof(mirror)); 255a7fbbf91SMatthew Dillon hammer_key_beg_init(&mirror.key_beg); 256a7fbbf91SMatthew Dillon hammer_key_end_init(&mirror.key_end); 25717dd83bcSMatthew Dillon mirror.key_end = mirror.key_beg; 258a7fbbf91SMatthew Dillon 259d4e5b69bSMatthew Dillon fd = getpfs(&pfs, filesystem); 260a7fbbf91SMatthew Dillon 261243ca327SMatthew Dillon /* 262d4e5b69bSMatthew Dillon * In two-way mode the target writes out a PFS packet first. 263d4e5b69bSMatthew Dillon * The source uses our tid_end as its tid_beg by default, 264d4e5b69bSMatthew Dillon * picking up where it left off. 265243ca327SMatthew Dillon */ 266d4e5b69bSMatthew Dillon mirror.tid_beg = 0; 267d4e5b69bSMatthew Dillon if (TwoWayPipeOpt) { 268d4e5b69bSMatthew Dillon generate_mrec_header(fd, 1, pfs.pfs_id, 269d4e5b69bSMatthew Dillon &mirror.tid_beg, &mirror.tid_end); 270d4e5b69bSMatthew Dillon } 271d4e5b69bSMatthew Dillon 272d4e5b69bSMatthew Dillon /* 27317dd83bcSMatthew Dillon * Read and process the PFS header. The source informs us of 27417dd83bcSMatthew Dillon * the TID range the stream represents. 275d4e5b69bSMatthew Dillon */ 276d4e5b69bSMatthew Dillon validate_mrec_header(fd, 0, 1, pfs.pfs_id, 277d4e5b69bSMatthew Dillon &mirror.tid_beg, &mirror.tid_end); 27834ebae70SMatthew Dillon 279a7fbbf91SMatthew Dillon mirror.ubuf = buf; 280a7fbbf91SMatthew Dillon mirror.size = SERIALBUF_SIZE; 281a7fbbf91SMatthew Dillon 282a7fbbf91SMatthew Dillon pickup.signature = 0; 283243ca327SMatthew Dillon pickup.type = 0; 284a7fbbf91SMatthew Dillon 285243ca327SMatthew Dillon /* 28617dd83bcSMatthew Dillon * Read and process bulk records (REC, PASS, and SKIP types). 28717dd83bcSMatthew Dillon * 28817dd83bcSMatthew Dillon * On your life, do NOT mess with mirror.key_cur or your mirror 28917dd83bcSMatthew Dillon * target may become history. 290243ca327SMatthew Dillon */ 291a7fbbf91SMatthew Dillon for (;;) { 292a7fbbf91SMatthew Dillon mirror.count = 0; 293d4e5b69bSMatthew Dillon mirror.pfs_id = pfs.pfs_id; 294d4e5b69bSMatthew Dillon mirror.shared_uuid = pfs.ondisk->shared_uuid; 295a7fbbf91SMatthew Dillon mirror.size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup); 296a7fbbf91SMatthew Dillon if (mirror.size <= 0) 297a7fbbf91SMatthew Dillon break; 298a7fbbf91SMatthew Dillon if (ioctl(fd, HAMMERIOC_MIRROR_WRITE, &mirror) < 0) { 299a7fbbf91SMatthew Dillon fprintf(stderr, "Mirror-write %s failed: %s\n", 300a7fbbf91SMatthew Dillon filesystem, strerror(errno)); 301a7fbbf91SMatthew Dillon exit(1); 302a7fbbf91SMatthew Dillon } 30317dd83bcSMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) { 30417dd83bcSMatthew Dillon fprintf(stderr, 30517dd83bcSMatthew Dillon "Mirror-write %s fatal error %d\n", 30617dd83bcSMatthew Dillon filesystem, mirror.head.error); 30717dd83bcSMatthew Dillon exit(1); 30817dd83bcSMatthew Dillon } 309243ca327SMatthew Dillon #if 0 310a7fbbf91SMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_INTR) { 311a7fbbf91SMatthew Dillon fprintf(stderr, 312a7fbbf91SMatthew Dillon "Mirror-write %s interrupted by timer at" 313243ca327SMatthew Dillon " %016llx\n", 314a7fbbf91SMatthew Dillon filesystem, 315243ca327SMatthew Dillon mirror.key_cur.obj_id); 316a7fbbf91SMatthew Dillon exit(0); 317a7fbbf91SMatthew Dillon } 318243ca327SMatthew Dillon #endif 319a7fbbf91SMatthew Dillon } 320243ca327SMatthew Dillon 321243ca327SMatthew Dillon /* 322243ca327SMatthew Dillon * Read and process the termination sync record. 323243ca327SMatthew Dillon */ 324243ca327SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 32517dd83bcSMatthew Dillon if (mrec == NULL || 32617dd83bcSMatthew Dillon mrec->head.type != HAMMER_MREC_TYPE_SYNC || 32717dd83bcSMatthew Dillon mrec->head.rec_size != sizeof(mrec->sync)) { 328243ca327SMatthew Dillon fprintf(stderr, "Mirror-write %s: Did not get termination " 32917dd83bcSMatthew Dillon "sync record, or rec_size is wrong rt=%d\n", 33017dd83bcSMatthew Dillon filesystem, mrec->head.type); 331243ca327SMatthew Dillon } 33217dd83bcSMatthew Dillon free(mrec); 33317dd83bcSMatthew Dillon mrec = NULL; 334243ca327SMatthew Dillon 335243ca327SMatthew Dillon /* 336243ca327SMatthew Dillon * Update the PFS info on the target so the user has visibility 337243ca327SMatthew Dillon * into the new snapshot. 338243ca327SMatthew Dillon */ 339d4e5b69bSMatthew Dillon update_pfs_snapshot(fd, mirror.tid_end, pfs.pfs_id); 340243ca327SMatthew Dillon 341243ca327SMatthew Dillon /* 342243ca327SMatthew Dillon * Sync the target filesystem 343243ca327SMatthew Dillon */ 344243ca327SMatthew Dillon bzero(&synctid, sizeof(synctid)); 345243ca327SMatthew Dillon synctid.op = HAMMER_SYNCTID_SYNC2; 346243ca327SMatthew Dillon ioctl(fd, HAMMERIOC_SYNCTID, &synctid); 347243ca327SMatthew Dillon 348243ca327SMatthew Dillon fprintf(stderr, "Mirror-write %s: succeeded\n", filesystem); 349243ca327SMatthew Dillon 350243ca327SMatthew Dillon /* 351243ca327SMatthew Dillon * Report back to the originator. 352243ca327SMatthew Dillon */ 353243ca327SMatthew Dillon if (TwoWayPipeOpt) { 35417dd83bcSMatthew Dillon mrec_tmp.update.tid = mirror.tid_end; 355243ca327SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_UPDATE, 35617dd83bcSMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.update)); 357243ca327SMatthew Dillon } else { 358243ca327SMatthew Dillon printf("Source can update synctid to 0x%016llx\n", 359243ca327SMatthew Dillon mirror.tid_end); 360243ca327SMatthew Dillon } 361243ca327SMatthew Dillon } 362243ca327SMatthew Dillon 363243ca327SMatthew Dillon void 364243ca327SMatthew Dillon hammer_cmd_mirror_dump(void) 365243ca327SMatthew Dillon { 366243ca327SMatthew Dillon char *buf = malloc(SERIALBUF_SIZE); 36717dd83bcSMatthew Dillon struct hammer_ioc_mrecord_head pickup; 36817dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 369243ca327SMatthew Dillon int error; 370243ca327SMatthew Dillon int size; 37117dd83bcSMatthew Dillon int offset; 37217dd83bcSMatthew Dillon int bytes; 373243ca327SMatthew Dillon 374243ca327SMatthew Dillon /* 375243ca327SMatthew Dillon * Read and process the PFS header 376243ca327SMatthew Dillon */ 377243ca327SMatthew Dillon pickup.signature = 0; 378243ca327SMatthew Dillon pickup.type = 0; 379243ca327SMatthew Dillon 380243ca327SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 381243ca327SMatthew Dillon 382243ca327SMatthew Dillon /* 383243ca327SMatthew Dillon * Read and process bulk records 384243ca327SMatthew Dillon */ 385243ca327SMatthew Dillon for (;;) { 386243ca327SMatthew Dillon size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup); 387243ca327SMatthew Dillon if (size <= 0) 388243ca327SMatthew Dillon break; 38917dd83bcSMatthew Dillon offset = 0; 39017dd83bcSMatthew Dillon while (offset < size) { 39117dd83bcSMatthew Dillon mrec = (void *)((char *)buf + offset); 39217dd83bcSMatthew Dillon bytes = HAMMER_HEAD_DOALIGN(mrec->head.rec_size); 39317dd83bcSMatthew Dillon if (offset + bytes > size) { 39417dd83bcSMatthew Dillon fprintf(stderr, "Misaligned record\n"); 39517dd83bcSMatthew Dillon exit(1); 39617dd83bcSMatthew Dillon } 39717dd83bcSMatthew Dillon 39817dd83bcSMatthew Dillon switch(mrec->head.type) { 39917dd83bcSMatthew Dillon case HAMMER_MREC_TYPE_REC: 400243ca327SMatthew Dillon printf("Record obj=%016llx key=%016llx " 401243ca327SMatthew Dillon "rt=%02x ot=%02x\n", 40217dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_id, 40317dd83bcSMatthew Dillon mrec->rec.leaf.base.key, 40417dd83bcSMatthew Dillon mrec->rec.leaf.base.rec_type, 40517dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_type); 406243ca327SMatthew Dillon printf(" tids %016llx:%016llx data=%d\n", 40717dd83bcSMatthew Dillon mrec->rec.leaf.base.create_tid, 40817dd83bcSMatthew Dillon mrec->rec.leaf.base.delete_tid, 40917dd83bcSMatthew Dillon mrec->rec.leaf.data_len); 41017dd83bcSMatthew Dillon break; 41117dd83bcSMatthew Dillon case HAMMER_MREC_TYPE_PASS: 41217dd83bcSMatthew Dillon printf("Pass obj=%016llx key=%016llx " 41317dd83bcSMatthew Dillon "rt=%02x ot=%02x\n", 41417dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_id, 41517dd83bcSMatthew Dillon mrec->rec.leaf.base.key, 41617dd83bcSMatthew Dillon mrec->rec.leaf.base.rec_type, 41717dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_type); 41817dd83bcSMatthew Dillon printf(" tids %016llx:%016llx data=%d\n", 41917dd83bcSMatthew Dillon mrec->rec.leaf.base.create_tid, 42017dd83bcSMatthew Dillon mrec->rec.leaf.base.delete_tid, 42117dd83bcSMatthew Dillon mrec->rec.leaf.data_len); 42217dd83bcSMatthew Dillon break; 42317dd83bcSMatthew Dillon case HAMMER_MREC_TYPE_SKIP: 42417dd83bcSMatthew Dillon printf("Skip obj=%016llx key=%016llx rt=%02x to\n" 42517dd83bcSMatthew Dillon " obj=%016llx key=%016llx rt=%02x\n", 42617dd83bcSMatthew Dillon mrec->skip.skip_beg.obj_id, 42717dd83bcSMatthew Dillon mrec->skip.skip_beg.key, 42817dd83bcSMatthew Dillon mrec->skip.skip_beg.rec_type, 42917dd83bcSMatthew Dillon mrec->skip.skip_end.obj_id, 43017dd83bcSMatthew Dillon mrec->skip.skip_end.key, 43117dd83bcSMatthew Dillon mrec->skip.skip_end.rec_type); 43217dd83bcSMatthew Dillon default: 43317dd83bcSMatthew Dillon break; 43417dd83bcSMatthew Dillon } 43517dd83bcSMatthew Dillon offset += bytes; 436243ca327SMatthew Dillon } 437243ca327SMatthew Dillon } 438243ca327SMatthew Dillon 439243ca327SMatthew Dillon /* 440243ca327SMatthew Dillon * Read and process the termination sync record. 441243ca327SMatthew Dillon */ 442243ca327SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 44317dd83bcSMatthew Dillon if (mrec == NULL || mrec->head.type != HAMMER_MREC_TYPE_SYNC) { 444243ca327SMatthew Dillon fprintf(stderr, "Mirror-dump: Did not get termination " 445243ca327SMatthew Dillon "sync record\n"); 446243ca327SMatthew Dillon } 447a7fbbf91SMatthew Dillon } 448a7fbbf91SMatthew Dillon 449a7fbbf91SMatthew Dillon void 450a7fbbf91SMatthew Dillon hammer_cmd_mirror_copy(char **av, int ac) 451a7fbbf91SMatthew Dillon { 45234ebae70SMatthew Dillon pid_t pid1; 45334ebae70SMatthew Dillon pid_t pid2; 45434ebae70SMatthew Dillon int fds[2]; 455243ca327SMatthew Dillon const char *xav[16]; 456243ca327SMatthew Dillon char tbuf[16]; 45734ebae70SMatthew Dillon char *ptr; 458243ca327SMatthew Dillon int xac; 45934ebae70SMatthew Dillon 46034ebae70SMatthew Dillon if (ac != 2) 46134ebae70SMatthew Dillon mirror_usage(1); 46234ebae70SMatthew Dillon 46334ebae70SMatthew Dillon if (pipe(fds) < 0) { 46434ebae70SMatthew Dillon perror("pipe"); 46534ebae70SMatthew Dillon exit(1); 46634ebae70SMatthew Dillon } 46734ebae70SMatthew Dillon 468243ca327SMatthew Dillon TwoWayPipeOpt = 1; 469243ca327SMatthew Dillon 47034ebae70SMatthew Dillon /* 47134ebae70SMatthew Dillon * Source 47234ebae70SMatthew Dillon */ 47334ebae70SMatthew Dillon if ((pid1 = fork()) == 0) { 47434ebae70SMatthew Dillon dup2(fds[0], 0); 47534ebae70SMatthew Dillon dup2(fds[0], 1); 47634ebae70SMatthew Dillon close(fds[0]); 47734ebae70SMatthew Dillon close(fds[1]); 47834ebae70SMatthew Dillon if ((ptr = strchr(av[0], ':')) != NULL) { 47934ebae70SMatthew Dillon *ptr++ = 0; 480243ca327SMatthew Dillon xac = 0; 481243ca327SMatthew Dillon xav[xac++] = "ssh"; 482243ca327SMatthew Dillon xav[xac++] = av[0]; 483243ca327SMatthew Dillon xav[xac++] = "hammer"; 484243ca327SMatthew Dillon if (VerboseOpt) 485243ca327SMatthew Dillon xav[xac++] = "-v"; 486243ca327SMatthew Dillon xav[xac++] = "-2"; 487243ca327SMatthew Dillon if (TimeoutOpt) { 488243ca327SMatthew Dillon snprintf(tbuf, sizeof(tbuf), "%d", TimeoutOpt); 489243ca327SMatthew Dillon xav[xac++] = "-t"; 490243ca327SMatthew Dillon xav[xac++] = tbuf; 491243ca327SMatthew Dillon } 492243ca327SMatthew Dillon xav[xac++] = "mirror-read"; 493243ca327SMatthew Dillon xav[xac++] = ptr; 494243ca327SMatthew Dillon xav[xac++] = NULL; 495243ca327SMatthew Dillon execv("/usr/bin/ssh", (void *)xav); 49634ebae70SMatthew Dillon } else { 49734ebae70SMatthew Dillon hammer_cmd_mirror_read(av, 1); 498243ca327SMatthew Dillon fflush(stdout); 499243ca327SMatthew Dillon fflush(stderr); 50034ebae70SMatthew Dillon } 50153d93cc7SMatthew Dillon _exit(1); 50234ebae70SMatthew Dillon } 50334ebae70SMatthew Dillon 50434ebae70SMatthew Dillon /* 50534ebae70SMatthew Dillon * Target 50634ebae70SMatthew Dillon */ 50734ebae70SMatthew Dillon if ((pid2 = fork()) == 0) { 50834ebae70SMatthew Dillon dup2(fds[1], 0); 50934ebae70SMatthew Dillon dup2(fds[1], 1); 51034ebae70SMatthew Dillon close(fds[0]); 51134ebae70SMatthew Dillon close(fds[1]); 51234ebae70SMatthew Dillon if ((ptr = strchr(av[1], ':')) != NULL) { 51334ebae70SMatthew Dillon *ptr++ = 0; 514243ca327SMatthew Dillon xac = 0; 515243ca327SMatthew Dillon xav[xac++] = "ssh"; 516243ca327SMatthew Dillon xav[xac++] = av[1]; 517243ca327SMatthew Dillon xav[xac++] = "hammer"; 518243ca327SMatthew Dillon if (VerboseOpt) 519243ca327SMatthew Dillon xav[xac++] = "-v"; 520243ca327SMatthew Dillon xav[xac++] = "-2"; 521243ca327SMatthew Dillon xav[xac++] = "mirror-write"; 522243ca327SMatthew Dillon xav[xac++] = ptr; 523243ca327SMatthew Dillon xav[xac++] = NULL; 524243ca327SMatthew Dillon execv("/usr/bin/ssh", (void *)xav); 52534ebae70SMatthew Dillon } else { 52634ebae70SMatthew Dillon hammer_cmd_mirror_write(av + 1, 1); 527243ca327SMatthew Dillon fflush(stdout); 528243ca327SMatthew Dillon fflush(stderr); 52934ebae70SMatthew Dillon } 53053d93cc7SMatthew Dillon _exit(1); 53134ebae70SMatthew Dillon } 53234ebae70SMatthew Dillon close(fds[0]); 53334ebae70SMatthew Dillon close(fds[1]); 53434ebae70SMatthew Dillon 53534ebae70SMatthew Dillon while (waitpid(pid1, NULL, 0) <= 0) 53634ebae70SMatthew Dillon ; 53734ebae70SMatthew Dillon while (waitpid(pid2, NULL, 0) <= 0) 53834ebae70SMatthew Dillon ; 539a7fbbf91SMatthew Dillon } 540a7fbbf91SMatthew Dillon 541243ca327SMatthew Dillon /* 542243ca327SMatthew Dillon * Read and return multiple mrecords 543243ca327SMatthew Dillon */ 544a7fbbf91SMatthew Dillon static int 54517dd83bcSMatthew Dillon read_mrecords(int fd, char *buf, u_int size, hammer_ioc_mrecord_head_t pickup) 546a7fbbf91SMatthew Dillon { 54717dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 548a7fbbf91SMatthew Dillon u_int count; 549a7fbbf91SMatthew Dillon size_t n; 550a7fbbf91SMatthew Dillon size_t i; 55117dd83bcSMatthew Dillon size_t bytes; 552a7fbbf91SMatthew Dillon 553a7fbbf91SMatthew Dillon count = 0; 554a7fbbf91SMatthew Dillon while (size - count >= HAMMER_MREC_HEADSIZE) { 555a7fbbf91SMatthew Dillon /* 556a7fbbf91SMatthew Dillon * Cached the record header in case we run out of buffer 557a7fbbf91SMatthew Dillon * space. 558a7fbbf91SMatthew Dillon */ 55917dd83bcSMatthew Dillon fflush(stdout); 560a7fbbf91SMatthew Dillon if (pickup->signature == 0) { 561a7fbbf91SMatthew Dillon for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) { 562a7fbbf91SMatthew Dillon i = read(fd, (char *)pickup + n, 563a7fbbf91SMatthew Dillon HAMMER_MREC_HEADSIZE - n); 564a7fbbf91SMatthew Dillon if (i <= 0) 565a7fbbf91SMatthew Dillon break; 566a7fbbf91SMatthew Dillon } 567a7fbbf91SMatthew Dillon if (n == 0) 568a7fbbf91SMatthew Dillon break; 569a7fbbf91SMatthew Dillon if (n != HAMMER_MREC_HEADSIZE) { 570a7fbbf91SMatthew Dillon fprintf(stderr, "read_mrecords: short read on pipe\n"); 571a7fbbf91SMatthew Dillon exit(1); 572a7fbbf91SMatthew Dillon } 573a7fbbf91SMatthew Dillon 574a7fbbf91SMatthew Dillon if (pickup->signature != HAMMER_IOC_MIRROR_SIGNATURE) { 575a7fbbf91SMatthew Dillon fprintf(stderr, "read_mrecords: malformed record on pipe, bad signature\n"); 576a7fbbf91SMatthew Dillon exit(1); 577a7fbbf91SMatthew Dillon } 578a7fbbf91SMatthew Dillon } 579a7fbbf91SMatthew Dillon if (pickup->rec_size < HAMMER_MREC_HEADSIZE || 58017dd83bcSMatthew Dillon pickup->rec_size > sizeof(*mrec) + HAMMER_XBUFSIZE) { 581a7fbbf91SMatthew Dillon fprintf(stderr, "read_mrecords: malformed record on pipe, illegal rec_size\n"); 582a7fbbf91SMatthew Dillon exit(1); 583a7fbbf91SMatthew Dillon } 584a7fbbf91SMatthew Dillon 585a7fbbf91SMatthew Dillon /* 586a7fbbf91SMatthew Dillon * Stop if we have insufficient space for the record and data. 587a7fbbf91SMatthew Dillon */ 58817dd83bcSMatthew Dillon bytes = HAMMER_HEAD_DOALIGN(pickup->rec_size); 58917dd83bcSMatthew Dillon if (size - count < bytes) 590a7fbbf91SMatthew Dillon break; 591a7fbbf91SMatthew Dillon 592a7fbbf91SMatthew Dillon /* 59317dd83bcSMatthew Dillon * Stop if the record type is not a REC or a SKIP (the only 59417dd83bcSMatthew Dillon * two types the ioctl supports. Other types are used only 59517dd83bcSMatthew Dillon * by the userland protocol). 596243ca327SMatthew Dillon */ 59717dd83bcSMatthew Dillon if (pickup->type != HAMMER_MREC_TYPE_REC && 59817dd83bcSMatthew Dillon pickup->type != HAMMER_MREC_TYPE_SKIP && 59917dd83bcSMatthew Dillon pickup->type != HAMMER_MREC_TYPE_PASS) { 600243ca327SMatthew Dillon break; 60117dd83bcSMatthew Dillon } 602243ca327SMatthew Dillon 603243ca327SMatthew Dillon /* 604a7fbbf91SMatthew Dillon * Read the remainder and clear the pickup signature. 605a7fbbf91SMatthew Dillon */ 60617dd83bcSMatthew Dillon for (n = HAMMER_MREC_HEADSIZE; n < bytes; n += i) { 60717dd83bcSMatthew Dillon i = read(fd, buf + count + n, bytes - n); 608a7fbbf91SMatthew Dillon if (i <= 0) 609a7fbbf91SMatthew Dillon break; 610a7fbbf91SMatthew Dillon } 61117dd83bcSMatthew Dillon if (n != bytes) { 612a7fbbf91SMatthew Dillon fprintf(stderr, "read_mrecords: short read on pipe\n"); 613a7fbbf91SMatthew Dillon exit(1); 614a7fbbf91SMatthew Dillon } 61517dd83bcSMatthew Dillon 61617dd83bcSMatthew Dillon bcopy(pickup, buf + count, HAMMER_MREC_HEADSIZE); 61717dd83bcSMatthew Dillon pickup->signature = 0; 61817dd83bcSMatthew Dillon pickup->type = 0; 61917dd83bcSMatthew Dillon mrec = (void *)(buf + count); 62017dd83bcSMatthew Dillon 62117dd83bcSMatthew Dillon /* 62217dd83bcSMatthew Dillon * Validate the completed record 62317dd83bcSMatthew Dillon */ 62417dd83bcSMatthew Dillon if (mrec->head.rec_crc != 62517dd83bcSMatthew Dillon crc32((char *)mrec + HAMMER_MREC_CRCOFF, 62617dd83bcSMatthew Dillon mrec->head.rec_size - HAMMER_MREC_CRCOFF)) { 62717dd83bcSMatthew Dillon fprintf(stderr, "read_mrecords: malformed record " 62817dd83bcSMatthew Dillon "on pipe, bad crc\n"); 62917dd83bcSMatthew Dillon exit(1); 630a7fbbf91SMatthew Dillon } 631a7fbbf91SMatthew Dillon 63217dd83bcSMatthew Dillon /* 63317dd83bcSMatthew Dillon * If its a B-Tree record validate the data crc 63417dd83bcSMatthew Dillon */ 63517dd83bcSMatthew Dillon if (mrec->head.type == HAMMER_MREC_TYPE_REC) { 63617dd83bcSMatthew Dillon if (mrec->head.rec_size < 63717dd83bcSMatthew Dillon sizeof(mrec->rec) + mrec->rec.leaf.data_len) { 63817dd83bcSMatthew Dillon fprintf(stderr, 63917dd83bcSMatthew Dillon "read_mrecords: malformed record on " 64017dd83bcSMatthew Dillon "pipe, illegal element data_len\n"); 64117dd83bcSMatthew Dillon exit(1); 64217dd83bcSMatthew Dillon } 64317dd83bcSMatthew Dillon if (mrec->rec.leaf.data_len && 64417dd83bcSMatthew Dillon mrec->rec.leaf.data_offset && 64517dd83bcSMatthew Dillon hammer_crc_test_leaf(&mrec->rec + 1, &mrec->rec.leaf) == 0) { 64617dd83bcSMatthew Dillon fprintf(stderr, 64717dd83bcSMatthew Dillon "read_mrecords: data_crc did not " 64817dd83bcSMatthew Dillon "match data! obj=%016llx key=%016llx\n", 64917dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_id, 65017dd83bcSMatthew Dillon mrec->rec.leaf.base.key); 65117dd83bcSMatthew Dillon fprintf(stderr, 65217dd83bcSMatthew Dillon "continuing, but there are problems\n"); 65317dd83bcSMatthew Dillon } 65417dd83bcSMatthew Dillon } 65517dd83bcSMatthew Dillon count += bytes; 656a7fbbf91SMatthew Dillon } 657a7fbbf91SMatthew Dillon return(count); 658a7fbbf91SMatthew Dillon } 659a7fbbf91SMatthew Dillon 66034ebae70SMatthew Dillon /* 66117dd83bcSMatthew Dillon * Read and return a single mrecord. 662243ca327SMatthew Dillon */ 663243ca327SMatthew Dillon static 66417dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t 66517dd83bcSMatthew Dillon read_mrecord(int fdin, int *errorp, hammer_ioc_mrecord_head_t pickup) 666243ca327SMatthew Dillon { 66717dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 66817dd83bcSMatthew Dillon struct hammer_ioc_mrecord_head mrechd; 669243ca327SMatthew Dillon size_t bytes; 670243ca327SMatthew Dillon size_t n; 671243ca327SMatthew Dillon size_t i; 672243ca327SMatthew Dillon 673243ca327SMatthew Dillon if (pickup && pickup->type != 0) { 674243ca327SMatthew Dillon mrechd = *pickup; 675243ca327SMatthew Dillon pickup->signature = 0; 676243ca327SMatthew Dillon pickup->type = 0; 677243ca327SMatthew Dillon n = HAMMER_MREC_HEADSIZE; 678243ca327SMatthew Dillon } else { 679243ca327SMatthew Dillon /* 680243ca327SMatthew Dillon * Read in the PFSD header from the sender. 681243ca327SMatthew Dillon */ 682243ca327SMatthew Dillon for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) { 683243ca327SMatthew Dillon i = read(fdin, (char *)&mrechd + n, HAMMER_MREC_HEADSIZE - n); 684243ca327SMatthew Dillon if (i <= 0) 685243ca327SMatthew Dillon break; 686243ca327SMatthew Dillon } 687243ca327SMatthew Dillon if (n == 0) { 688243ca327SMatthew Dillon *errorp = 0; /* EOF */ 689243ca327SMatthew Dillon return(NULL); 690243ca327SMatthew Dillon } 691243ca327SMatthew Dillon if (n != HAMMER_MREC_HEADSIZE) { 692243ca327SMatthew Dillon fprintf(stderr, "short read of mrecord header\n"); 693243ca327SMatthew Dillon *errorp = EPIPE; 694243ca327SMatthew Dillon return(NULL); 695243ca327SMatthew Dillon } 696243ca327SMatthew Dillon } 697243ca327SMatthew Dillon if (mrechd.signature != HAMMER_IOC_MIRROR_SIGNATURE) { 698243ca327SMatthew Dillon fprintf(stderr, "read_mrecord: bad signature\n"); 699243ca327SMatthew Dillon *errorp = EINVAL; 700243ca327SMatthew Dillon return(NULL); 701243ca327SMatthew Dillon } 70217dd83bcSMatthew Dillon bytes = HAMMER_HEAD_DOALIGN(mrechd.rec_size); 70317dd83bcSMatthew Dillon assert(bytes >= sizeof(mrechd)); 704243ca327SMatthew Dillon mrec = malloc(bytes); 70517dd83bcSMatthew Dillon mrec->head = mrechd; 70617dd83bcSMatthew Dillon 707243ca327SMatthew Dillon while (n < bytes) { 708243ca327SMatthew Dillon i = read(fdin, (char *)mrec + n, bytes - n); 709243ca327SMatthew Dillon if (i <= 0) 710243ca327SMatthew Dillon break; 711243ca327SMatthew Dillon n += i; 712243ca327SMatthew Dillon } 713243ca327SMatthew Dillon if (n != bytes) { 714243ca327SMatthew Dillon fprintf(stderr, "read_mrecord: short read on payload\n"); 715243ca327SMatthew Dillon *errorp = EPIPE; 716243ca327SMatthew Dillon return(NULL); 717243ca327SMatthew Dillon } 71817dd83bcSMatthew Dillon if (mrec->head.rec_crc != 71917dd83bcSMatthew Dillon crc32((char *)mrec + HAMMER_MREC_CRCOFF, 72017dd83bcSMatthew Dillon mrec->head.rec_size - HAMMER_MREC_CRCOFF)) { 721243ca327SMatthew Dillon fprintf(stderr, "read_mrecord: bad CRC\n"); 722243ca327SMatthew Dillon *errorp = EINVAL; 723243ca327SMatthew Dillon return(NULL); 724243ca327SMatthew Dillon } 725243ca327SMatthew Dillon *errorp = 0; 726243ca327SMatthew Dillon return(mrec); 727243ca327SMatthew Dillon } 728243ca327SMatthew Dillon 729243ca327SMatthew Dillon static 730243ca327SMatthew Dillon void 73117dd83bcSMatthew Dillon write_mrecord(int fdout, u_int32_t type, hammer_ioc_mrecord_any_t mrec, 73217dd83bcSMatthew Dillon int bytes) 733243ca327SMatthew Dillon { 73417dd83bcSMatthew Dillon char zbuf[HAMMER_HEAD_ALIGN]; 73517dd83bcSMatthew Dillon int pad; 736243ca327SMatthew Dillon 73717dd83bcSMatthew Dillon pad = HAMMER_HEAD_DOALIGN(bytes) - bytes; 73817dd83bcSMatthew Dillon 73917dd83bcSMatthew Dillon assert(bytes >= (int)sizeof(mrec->head)); 74017dd83bcSMatthew Dillon bzero(&mrec->head, sizeof(mrec->head)); 74117dd83bcSMatthew Dillon mrec->head.signature = HAMMER_IOC_MIRROR_SIGNATURE; 74217dd83bcSMatthew Dillon mrec->head.type = type; 74317dd83bcSMatthew Dillon mrec->head.rec_size = bytes; 74417dd83bcSMatthew Dillon mrec->head.rec_crc = crc32((char *)mrec + HAMMER_MREC_CRCOFF, 74517dd83bcSMatthew Dillon bytes - HAMMER_MREC_CRCOFF); 74617dd83bcSMatthew Dillon if (write(fdout, mrec, bytes) != bytes) { 747243ca327SMatthew Dillon fprintf(stderr, "write_mrecord: error %d (%s)\n", 748243ca327SMatthew Dillon errno, strerror(errno)); 749243ca327SMatthew Dillon exit(1); 750243ca327SMatthew Dillon } 75117dd83bcSMatthew Dillon if (pad) { 75217dd83bcSMatthew Dillon bzero(zbuf, pad); 75317dd83bcSMatthew Dillon if (write(fdout, zbuf, pad) != pad) { 75417dd83bcSMatthew Dillon fprintf(stderr, "write_mrecord: error %d (%s)\n", 75517dd83bcSMatthew Dillon errno, strerror(errno)); 75617dd83bcSMatthew Dillon exit(1); 75717dd83bcSMatthew Dillon } 75817dd83bcSMatthew Dillon } 759243ca327SMatthew Dillon } 760243ca327SMatthew Dillon 761243ca327SMatthew Dillon /* 76234ebae70SMatthew Dillon * Generate a mirroring header with the pfs information of the 76334ebae70SMatthew Dillon * originating filesytem. 76434ebae70SMatthew Dillon */ 76534ebae70SMatthew Dillon static void 766d4e5b69bSMatthew Dillon generate_mrec_header(int fd, int fdout, int pfs_id, 76734ebae70SMatthew Dillon hammer_tid_t *tid_begp, hammer_tid_t *tid_endp) 76834ebae70SMatthew Dillon { 76934ebae70SMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 77017dd83bcSMatthew Dillon union hammer_ioc_mrecord_any mrec_tmp; 77134ebae70SMatthew Dillon 77234ebae70SMatthew Dillon bzero(&pfs, sizeof(pfs)); 77317dd83bcSMatthew Dillon bzero(&mrec_tmp, sizeof(mrec_tmp)); 774d4e5b69bSMatthew Dillon pfs.pfs_id = pfs_id; 77517dd83bcSMatthew Dillon pfs.ondisk = &mrec_tmp.pfs.pfsd; 77617dd83bcSMatthew Dillon pfs.bytes = sizeof(mrec_tmp.pfs.pfsd); 77734ebae70SMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 77834ebae70SMatthew Dillon fprintf(stderr, "mirror-read: not a HAMMER fs/pseudofs!\n"); 77934ebae70SMatthew Dillon exit(1); 78034ebae70SMatthew Dillon } 78134ebae70SMatthew Dillon if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) { 78234ebae70SMatthew Dillon fprintf(stderr, "mirror-read: HAMMER pfs version mismatch!\n"); 78334ebae70SMatthew Dillon exit(1); 78434ebae70SMatthew Dillon } 78534ebae70SMatthew Dillon 78634ebae70SMatthew Dillon /* 78734ebae70SMatthew Dillon * sync_beg_tid - lowest TID on source after which a full history 78834ebae70SMatthew Dillon * is available. 78934ebae70SMatthew Dillon * 79034ebae70SMatthew Dillon * sync_end_tid - highest fully synchronized TID from source. 79134ebae70SMatthew Dillon */ 79217dd83bcSMatthew Dillon if (tid_begp && *tid_begp < mrec_tmp.pfs.pfsd.sync_beg_tid) 79317dd83bcSMatthew Dillon *tid_begp = mrec_tmp.pfs.pfsd.sync_beg_tid; 794d4e5b69bSMatthew Dillon if (tid_endp) 79517dd83bcSMatthew Dillon *tid_endp = mrec_tmp.pfs.pfsd.sync_end_tid; 79617dd83bcSMatthew Dillon mrec_tmp.pfs.version = pfs.version; 797243ca327SMatthew Dillon write_mrecord(fdout, HAMMER_MREC_TYPE_PFSD, 79817dd83bcSMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.pfs)); 79934ebae70SMatthew Dillon } 80034ebae70SMatthew Dillon 80134ebae70SMatthew Dillon /* 80234ebae70SMatthew Dillon * Validate the pfs information from the originating filesystem 80334ebae70SMatthew Dillon * against the target filesystem. shared_uuid must match. 80434ebae70SMatthew Dillon */ 80534ebae70SMatthew Dillon static void 806d4e5b69bSMatthew Dillon validate_mrec_header(int fd, int fdin, int is_target, int pfs_id, 80734ebae70SMatthew Dillon hammer_tid_t *tid_begp, hammer_tid_t *tid_endp) 80834ebae70SMatthew Dillon { 80934ebae70SMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 81034ebae70SMatthew Dillon struct hammer_pseudofs_data pfsd; 81117dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 812243ca327SMatthew Dillon int error; 81334ebae70SMatthew Dillon 81434ebae70SMatthew Dillon /* 81534ebae70SMatthew Dillon * Get the PFSD info from the target filesystem. 81634ebae70SMatthew Dillon */ 81734ebae70SMatthew Dillon bzero(&pfs, sizeof(pfs)); 81834ebae70SMatthew Dillon bzero(&pfsd, sizeof(pfsd)); 819d4e5b69bSMatthew Dillon pfs.pfs_id = pfs_id; 82034ebae70SMatthew Dillon pfs.ondisk = &pfsd; 82134ebae70SMatthew Dillon pfs.bytes = sizeof(pfsd); 82234ebae70SMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 82334ebae70SMatthew Dillon fprintf(stderr, "mirror-write: not a HAMMER fs/pseudofs!\n"); 82434ebae70SMatthew Dillon exit(1); 82534ebae70SMatthew Dillon } 82634ebae70SMatthew Dillon if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) { 82734ebae70SMatthew Dillon fprintf(stderr, "mirror-write: HAMMER pfs version mismatch!\n"); 82834ebae70SMatthew Dillon exit(1); 82934ebae70SMatthew Dillon } 83034ebae70SMatthew Dillon 831243ca327SMatthew Dillon mrec = read_mrecord(fdin, &error, NULL); 832243ca327SMatthew Dillon if (mrec == NULL) { 833243ca327SMatthew Dillon if (error == 0) 834243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: short read\n"); 83534ebae70SMatthew Dillon exit(1); 83634ebae70SMatthew Dillon } 83717dd83bcSMatthew Dillon if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) { 838243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: did not get expected " 839243ca327SMatthew Dillon "PFSD record type\n"); 84034ebae70SMatthew Dillon exit(1); 84134ebae70SMatthew Dillon } 84217dd83bcSMatthew Dillon if (mrec->head.rec_size != sizeof(mrec->pfs)) { 843243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: unexpected payload " 844243ca327SMatthew Dillon "size\n"); 84534ebae70SMatthew Dillon exit(1); 84634ebae70SMatthew Dillon } 84717dd83bcSMatthew Dillon if (mrec->pfs.version != pfs.version) { 848243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: Version mismatch\n"); 84934ebae70SMatthew Dillon exit(1); 85034ebae70SMatthew Dillon } 85134ebae70SMatthew Dillon 85234ebae70SMatthew Dillon /* 85334ebae70SMatthew Dillon * Whew. Ok, is the read PFS info compatible with the target? 85434ebae70SMatthew Dillon */ 85517dd83bcSMatthew Dillon if (bcmp(&mrec->pfs.pfsd.shared_uuid, &pfsd.shared_uuid, 85617dd83bcSMatthew Dillon sizeof(pfsd.shared_uuid)) != 0) { 85717dd83bcSMatthew Dillon fprintf(stderr, 85817dd83bcSMatthew Dillon "mirror-write: source and target have " 85917dd83bcSMatthew Dillon "different shared_uuid's!\n"); 86034ebae70SMatthew Dillon exit(1); 86134ebae70SMatthew Dillon } 862d4e5b69bSMatthew Dillon if (is_target && 863d4e5b69bSMatthew Dillon (pfsd.mirror_flags & HAMMER_PFSD_SLAVE) == 0) { 86434ebae70SMatthew Dillon fprintf(stderr, "mirror-write: target must be in slave mode\n"); 86534ebae70SMatthew Dillon exit(1); 86634ebae70SMatthew Dillon } 867d4e5b69bSMatthew Dillon if (tid_begp) 86817dd83bcSMatthew Dillon *tid_begp = mrec->pfs.pfsd.sync_beg_tid; 869d4e5b69bSMatthew Dillon if (tid_endp) 87017dd83bcSMatthew Dillon *tid_endp = mrec->pfs.pfsd.sync_end_tid; 871243ca327SMatthew Dillon free(mrec); 87234ebae70SMatthew Dillon } 87334ebae70SMatthew Dillon 87434ebae70SMatthew Dillon static void 875d4e5b69bSMatthew Dillon update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id) 87634ebae70SMatthew Dillon { 877243ca327SMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 878243ca327SMatthew Dillon struct hammer_pseudofs_data pfsd; 87934ebae70SMatthew Dillon 880243ca327SMatthew Dillon bzero(&pfs, sizeof(pfs)); 881243ca327SMatthew Dillon bzero(&pfsd, sizeof(pfsd)); 882d4e5b69bSMatthew Dillon pfs.pfs_id = pfs_id; 883243ca327SMatthew Dillon pfs.ondisk = &pfsd; 884243ca327SMatthew Dillon pfs.bytes = sizeof(pfsd); 885243ca327SMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 886243ca327SMatthew Dillon perror("update_pfs_snapshot (read)"); 887243ca327SMatthew Dillon exit(1); 88834ebae70SMatthew Dillon } 889*9c67b4d2SMatthew Dillon if (pfsd.sync_end_tid != snapshot_tid) { 890ddc8e722SMatthew Dillon pfsd.sync_end_tid = snapshot_tid; 891243ca327SMatthew Dillon if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) != 0) { 892243ca327SMatthew Dillon perror("update_pfs_snapshot (rewrite)"); 893243ca327SMatthew Dillon exit(1); 89434ebae70SMatthew Dillon } 895243ca327SMatthew Dillon } 896*9c67b4d2SMatthew Dillon } 897243ca327SMatthew Dillon 89834ebae70SMatthew Dillon 899a7fbbf91SMatthew Dillon static void 900a7fbbf91SMatthew Dillon mirror_usage(int code) 901a7fbbf91SMatthew Dillon { 902a7fbbf91SMatthew Dillon fprintf(stderr, 903a7fbbf91SMatthew Dillon "hammer mirror-read <filesystem>\n" 904a7fbbf91SMatthew Dillon "hammer mirror-write <filesystem>\n" 905243ca327SMatthew Dillon "hammer mirror-dump\n" 906a7fbbf91SMatthew Dillon "hammer mirror-copy [[user@]host:]fs [[user@]host:]fs\n" 907a7fbbf91SMatthew Dillon ); 908a7fbbf91SMatthew Dillon exit(code); 909a7fbbf91SMatthew Dillon } 910