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*48eadef9SMatthew Dillon * $DragonFly: src/sbin/hammer/cmd_mirror.c,v 1.12 2008/07/31 06:01:31 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); 49*48eadef9SMatthew Dillon static int validate_mrec_header(int fd, int fdin, int is_target, int pfs_id, 50*48eadef9SMatthew Dillon struct hammer_ioc_mrecord_head *pickup, 5134ebae70SMatthew Dillon hammer_tid_t *tid_begp, hammer_tid_t *tid_endp); 52d4e5b69bSMatthew Dillon static void update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id); 53*48eadef9SMatthew Dillon static ssize_t writebw(int fd, const void *buf, size_t nbytes, 54*48eadef9SMatthew Dillon u_int64_t *bwcount, struct timeval *tv1); 55a7fbbf91SMatthew Dillon static void mirror_usage(int code); 56a7fbbf91SMatthew Dillon 5717dd83bcSMatthew Dillon /* 5817dd83bcSMatthew Dillon * Generate a mirroring data stream from the specific source over the 5917dd83bcSMatthew Dillon * entire key range, but restricted to the specified transaction range. 6017dd83bcSMatthew Dillon * 6117dd83bcSMatthew Dillon * The HAMMER VFS does most of the work, we add a few new mrecord 6217dd83bcSMatthew Dillon * types to negotiate the TID ranges and verify that the entire 6317dd83bcSMatthew Dillon * stream made it to the destination. 6417dd83bcSMatthew Dillon */ 65a7fbbf91SMatthew Dillon void 66*48eadef9SMatthew Dillon hammer_cmd_mirror_read(char **av, int ac, int streaming) 67a7fbbf91SMatthew Dillon { 68a7fbbf91SMatthew Dillon struct hammer_ioc_mirror_rw mirror; 69d4e5b69bSMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 7017dd83bcSMatthew Dillon union hammer_ioc_mrecord_any mrec_tmp; 71*48eadef9SMatthew Dillon struct hammer_ioc_mrecord_head pickup; 7217dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 73243ca327SMatthew Dillon hammer_tid_t sync_tid; 74a7fbbf91SMatthew Dillon const char *filesystem; 75a7fbbf91SMatthew Dillon char *buf = malloc(SERIALBUF_SIZE); 76243ca327SMatthew Dillon int interrupted = 0; 77243ca327SMatthew Dillon int error; 78a7fbbf91SMatthew Dillon int fd; 79243ca327SMatthew Dillon int n; 80*48eadef9SMatthew Dillon int didwork; 81*48eadef9SMatthew Dillon int64_t total_bytes; 82243ca327SMatthew Dillon time_t base_t = time(NULL); 83*48eadef9SMatthew Dillon struct timeval bwtv; 84*48eadef9SMatthew Dillon u_int64_t bwcount; 85a7fbbf91SMatthew Dillon 86a7fbbf91SMatthew Dillon if (ac > 2) 87a7fbbf91SMatthew Dillon mirror_usage(1); 88a7fbbf91SMatthew Dillon filesystem = av[0]; 89a7fbbf91SMatthew Dillon 90*48eadef9SMatthew Dillon pickup.signature = 0; 91*48eadef9SMatthew Dillon pickup.type = 0; 92*48eadef9SMatthew Dillon 93*48eadef9SMatthew Dillon again: 94a7fbbf91SMatthew Dillon bzero(&mirror, sizeof(mirror)); 95a7fbbf91SMatthew Dillon hammer_key_beg_init(&mirror.key_beg); 96a7fbbf91SMatthew Dillon hammer_key_end_init(&mirror.key_end); 97a7fbbf91SMatthew Dillon 98d4e5b69bSMatthew Dillon fd = getpfs(&pfs, filesystem); 99a7fbbf91SMatthew Dillon 100*48eadef9SMatthew Dillon if (streaming && VerboseOpt) { 101*48eadef9SMatthew Dillon fprintf(stderr, "\nRunning"); 102*48eadef9SMatthew Dillon fflush(stderr); 103*48eadef9SMatthew Dillon } 104*48eadef9SMatthew Dillon total_bytes = 0; 105*48eadef9SMatthew Dillon gettimeofday(&bwtv, NULL); 106*48eadef9SMatthew Dillon bwcount = 0; 107*48eadef9SMatthew Dillon 108243ca327SMatthew Dillon /* 109d4e5b69bSMatthew Dillon * In 2-way mode the target will send us a PFS info packet 110d4e5b69bSMatthew Dillon * first. Use the target's current snapshot TID as our default 111d4e5b69bSMatthew Dillon * begin TID. 112243ca327SMatthew Dillon */ 113d4e5b69bSMatthew Dillon mirror.tid_beg = 0; 114*48eadef9SMatthew Dillon if (TwoWayPipeOpt) { 115*48eadef9SMatthew Dillon n = validate_mrec_header(fd, 0, 0, pfs.pfs_id, &pickup, 116d4e5b69bSMatthew Dillon NULL, &mirror.tid_beg); 117*48eadef9SMatthew Dillon if (n < 0) { /* got TERM record */ 118*48eadef9SMatthew Dillon relpfs(fd, &pfs); 119*48eadef9SMatthew Dillon return; 120*48eadef9SMatthew Dillon } 121*48eadef9SMatthew Dillon ++mirror.tid_beg; 122*48eadef9SMatthew Dillon } 123d4e5b69bSMatthew Dillon 124d4e5b69bSMatthew Dillon /* 125d4e5b69bSMatthew Dillon * Write out the PFS header, tid_beg will be updated if our PFS 126d4e5b69bSMatthew Dillon * has a larger begin sync. tid_end is set to the latest source 127d4e5b69bSMatthew Dillon * TID whos flush cycle has completed. 128d4e5b69bSMatthew Dillon */ 129d4e5b69bSMatthew Dillon generate_mrec_header(fd, 1, pfs.pfs_id, 130d4e5b69bSMatthew Dillon &mirror.tid_beg, &mirror.tid_end); 131d4e5b69bSMatthew Dillon 132*48eadef9SMatthew Dillon /* XXX streaming mode support w/ cycle or command line arg */ 133d4e5b69bSMatthew Dillon /* 134d4e5b69bSMatthew Dillon * A cycle file overrides the beginning TID 135d4e5b69bSMatthew Dillon */ 136243ca327SMatthew Dillon hammer_get_cycle(&mirror.key_beg, &mirror.tid_beg); 13734ebae70SMatthew Dillon 13817dd83bcSMatthew Dillon if (ac == 2) 13917dd83bcSMatthew Dillon mirror.tid_beg = strtoull(av[1], NULL, 0); 14017dd83bcSMatthew Dillon 141*48eadef9SMatthew Dillon if (streaming == 0 || VerboseOpt >= 2) { 142*48eadef9SMatthew Dillon fprintf(stderr, 143*48eadef9SMatthew Dillon "Mirror-read: Mirror from %016llx to %016llx\n", 144243ca327SMatthew Dillon mirror.tid_beg, mirror.tid_end); 145*48eadef9SMatthew Dillon } 146243ca327SMatthew Dillon if (mirror.key_beg.obj_id != (int64_t)HAMMER_MIN_OBJID) { 1479dc76cb1SMatthew Dillon fprintf(stderr, "Mirror-read: Resuming at object %016llx\n", 148243ca327SMatthew Dillon mirror.key_beg.obj_id); 149243ca327SMatthew Dillon } 150243ca327SMatthew Dillon 151243ca327SMatthew Dillon /* 1529dc76cb1SMatthew Dillon * Nothing to do if begin equals end. 1539dc76cb1SMatthew Dillon */ 154*48eadef9SMatthew Dillon if (mirror.tid_beg >= mirror.tid_end) { 155*48eadef9SMatthew Dillon if (streaming == 0 || VerboseOpt >= 2) 156*48eadef9SMatthew Dillon fprintf(stderr, "Mirror-read: No work to do\n"); 157*48eadef9SMatthew Dillon didwork = 0; 1589dc76cb1SMatthew Dillon goto done; 1599dc76cb1SMatthew Dillon } 160*48eadef9SMatthew Dillon didwork = 1; 1619dc76cb1SMatthew Dillon 1629dc76cb1SMatthew Dillon /* 163243ca327SMatthew Dillon * Write out bulk records 164243ca327SMatthew Dillon */ 165a7fbbf91SMatthew Dillon mirror.ubuf = buf; 166a7fbbf91SMatthew Dillon mirror.size = SERIALBUF_SIZE; 167a7fbbf91SMatthew Dillon 168a7fbbf91SMatthew Dillon do { 169a7fbbf91SMatthew Dillon mirror.count = 0; 170d4e5b69bSMatthew Dillon mirror.pfs_id = pfs.pfs_id; 171d4e5b69bSMatthew Dillon mirror.shared_uuid = pfs.ondisk->shared_uuid; 172a7fbbf91SMatthew Dillon if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) { 173a7fbbf91SMatthew Dillon fprintf(stderr, "Mirror-read %s failed: %s\n", 174a7fbbf91SMatthew Dillon filesystem, strerror(errno)); 175a7fbbf91SMatthew Dillon exit(1); 176a7fbbf91SMatthew Dillon } 1779c67b4d2SMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) { 1789c67b4d2SMatthew Dillon fprintf(stderr, 1799c67b4d2SMatthew Dillon "Mirror-read %s fatal error %d\n", 1809c67b4d2SMatthew Dillon filesystem, mirror.head.error); 1819c67b4d2SMatthew Dillon exit(1); 1829c67b4d2SMatthew Dillon } 183243ca327SMatthew Dillon if (mirror.count) { 184*48eadef9SMatthew Dillon if (BandwidthOpt) { 185*48eadef9SMatthew Dillon n = writebw(1, mirror.ubuf, mirror.count, 186*48eadef9SMatthew Dillon &bwcount, &bwtv); 187*48eadef9SMatthew Dillon } else { 188243ca327SMatthew Dillon n = write(1, mirror.ubuf, mirror.count); 189*48eadef9SMatthew Dillon } 190243ca327SMatthew Dillon if (n != mirror.count) { 191243ca327SMatthew Dillon fprintf(stderr, "Mirror-read %s failed: " 192243ca327SMatthew Dillon "short write\n", 193243ca327SMatthew Dillon filesystem); 194243ca327SMatthew Dillon exit(1); 195243ca327SMatthew Dillon } 196a7fbbf91SMatthew Dillon } 197*48eadef9SMatthew Dillon total_bytes += mirror.count; 198*48eadef9SMatthew Dillon if (streaming && VerboseOpt) { 199*48eadef9SMatthew Dillon fprintf(stderr, "\r%016llx %11lld", 200*48eadef9SMatthew Dillon mirror.key_cur.obj_id, 201*48eadef9SMatthew Dillon total_bytes); 202*48eadef9SMatthew Dillon fflush(stderr); 203*48eadef9SMatthew Dillon } 204a7fbbf91SMatthew Dillon mirror.key_beg = mirror.key_cur; 205243ca327SMatthew Dillon if (TimeoutOpt && 206243ca327SMatthew Dillon (unsigned)(time(NULL) - base_t) > (unsigned)TimeoutOpt) { 207243ca327SMatthew Dillon fprintf(stderr, 208243ca327SMatthew Dillon "Mirror-read %s interrupted by timer at" 209243ca327SMatthew Dillon " %016llx\n", 210243ca327SMatthew Dillon filesystem, 211243ca327SMatthew Dillon mirror.key_cur.obj_id); 212243ca327SMatthew Dillon interrupted = 1; 213243ca327SMatthew Dillon break; 214243ca327SMatthew Dillon } 215a7fbbf91SMatthew Dillon } while (mirror.count != 0); 216a7fbbf91SMatthew Dillon 217*48eadef9SMatthew Dillon done: 218243ca327SMatthew Dillon /* 219*48eadef9SMatthew Dillon * Write out the termination sync record - only if not interrupted 220243ca327SMatthew Dillon */ 221*48eadef9SMatthew Dillon if (interrupted == 0) { 222*48eadef9SMatthew Dillon if (didwork) { 22317dd83bcSMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_SYNC, 22417dd83bcSMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.sync)); 225*48eadef9SMatthew Dillon } else { 226*48eadef9SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_IDLE, 227*48eadef9SMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.sync)); 228*48eadef9SMatthew Dillon } 229*48eadef9SMatthew Dillon } 23034ebae70SMatthew Dillon 231243ca327SMatthew Dillon /* 232243ca327SMatthew Dillon * If the -2 option was given (automatic when doing mirror-copy), 233243ca327SMatthew Dillon * a two-way pipe is assumed and we expect a response mrec from 234243ca327SMatthew Dillon * the target. 235243ca327SMatthew Dillon */ 236243ca327SMatthew Dillon if (TwoWayPipeOpt) { 237*48eadef9SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 23817dd83bcSMatthew Dillon if (mrec == NULL || 23917dd83bcSMatthew Dillon mrec->head.type != HAMMER_MREC_TYPE_UPDATE || 24017dd83bcSMatthew Dillon mrec->head.rec_size != sizeof(mrec->update)) { 241243ca327SMatthew Dillon fprintf(stderr, "mirror_read: Did not get final " 242243ca327SMatthew Dillon "acknowledgement packet from target\n"); 243243ca327SMatthew Dillon exit(1); 244243ca327SMatthew Dillon } 245243ca327SMatthew Dillon if (interrupted) { 246243ca327SMatthew Dillon if (CyclePath) { 247243ca327SMatthew Dillon hammer_set_cycle(&mirror.key_cur, mirror.tid_beg); 248243ca327SMatthew Dillon fprintf(stderr, "Cyclefile %s updated for continuation\n", CyclePath); 249243ca327SMatthew Dillon } 250243ca327SMatthew Dillon } else { 25117dd83bcSMatthew Dillon sync_tid = mrec->update.tid; 252243ca327SMatthew Dillon if (CyclePath) { 253243ca327SMatthew Dillon hammer_key_beg_init(&mirror.key_beg); 254243ca327SMatthew Dillon hammer_set_cycle(&mirror.key_beg, sync_tid); 255243ca327SMatthew Dillon fprintf(stderr, "Cyclefile %s updated to 0x%016llx\n", 256243ca327SMatthew Dillon CyclePath, sync_tid); 257243ca327SMatthew Dillon } 258243ca327SMatthew Dillon } 259243ca327SMatthew Dillon } else if (CyclePath) { 260243ca327SMatthew Dillon /* NOTE! mirror.tid_beg cannot be updated */ 261243ca327SMatthew Dillon fprintf(stderr, "Warning: cycle file (-c option) cannot be " 262243ca327SMatthew Dillon "fully updated unless you use mirror-copy\n"); 263243ca327SMatthew Dillon hammer_set_cycle(&mirror.key_beg, mirror.tid_beg); 264243ca327SMatthew Dillon } 265*48eadef9SMatthew Dillon if (streaming && interrupted == 0) { 266*48eadef9SMatthew Dillon time_t t1 = time(NULL); 267*48eadef9SMatthew Dillon time_t t2; 268*48eadef9SMatthew Dillon 269*48eadef9SMatthew Dillon if (VerboseOpt) { 270*48eadef9SMatthew Dillon fprintf(stderr, " W"); 271*48eadef9SMatthew Dillon fflush(stderr); 272*48eadef9SMatthew Dillon } 273*48eadef9SMatthew Dillon pfs.ondisk->sync_end_tid = mirror.tid_end; 274*48eadef9SMatthew Dillon if (ioctl(fd, HAMMERIOC_WAI_PSEUDOFS, &pfs) < 0) { 275*48eadef9SMatthew Dillon fprintf(stderr, "Mirror-read %s: cannot stream: %s\n", 276*48eadef9SMatthew Dillon filesystem, strerror(errno)); 277*48eadef9SMatthew Dillon } else { 278*48eadef9SMatthew Dillon t2 = time(NULL) - t1; 279*48eadef9SMatthew Dillon if (t2 >= 0 && t2 < DelayOpt) { 280*48eadef9SMatthew Dillon if (VerboseOpt) { 281*48eadef9SMatthew Dillon fprintf(stderr, "\bD"); 282*48eadef9SMatthew Dillon fflush(stderr); 283*48eadef9SMatthew Dillon } 284*48eadef9SMatthew Dillon sleep(DelayOpt - t2); 285*48eadef9SMatthew Dillon } 286*48eadef9SMatthew Dillon if (VerboseOpt) { 287*48eadef9SMatthew Dillon fprintf(stderr, "\b "); 288*48eadef9SMatthew Dillon fflush(stderr); 289*48eadef9SMatthew Dillon } 290*48eadef9SMatthew Dillon relpfs(fd, &pfs); 291*48eadef9SMatthew Dillon goto again; 292*48eadef9SMatthew Dillon } 293*48eadef9SMatthew Dillon } 294*48eadef9SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_TERM, 295*48eadef9SMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.sync)); 296*48eadef9SMatthew Dillon relpfs(fd, &pfs); 297a7fbbf91SMatthew Dillon fprintf(stderr, "Mirror-read %s succeeded\n", filesystem); 298a7fbbf91SMatthew Dillon } 299a7fbbf91SMatthew Dillon 30017dd83bcSMatthew Dillon /* 30117dd83bcSMatthew Dillon * Pipe the mirroring data stream on stdin to the HAMMER VFS, adding 30217dd83bcSMatthew Dillon * some additional packet types to negotiate TID ranges and to verify 30317dd83bcSMatthew Dillon * completion. The HAMMER VFS does most of the work. 30417dd83bcSMatthew Dillon * 30517dd83bcSMatthew Dillon * It is important to note that the mirror.key_{beg,end} range must 30617dd83bcSMatthew Dillon * match the ranged used by the original. For now both sides use 30717dd83bcSMatthew Dillon * range the entire key space. 30817dd83bcSMatthew Dillon * 30917dd83bcSMatthew Dillon * It is even more important that the records in the stream conform 31017dd83bcSMatthew Dillon * to the TID range also supplied in the stream. The HAMMER VFS will 31117dd83bcSMatthew Dillon * use the REC, PASS, and SKIP record types to track the portions of 31217dd83bcSMatthew Dillon * the B-Tree being scanned in order to be able to proactively delete 31317dd83bcSMatthew Dillon * records on the target within those active areas that are not mentioned 31417dd83bcSMatthew Dillon * by the source. 31517dd83bcSMatthew Dillon * 31617dd83bcSMatthew Dillon * The mirror.key_cur field is used by the VFS to do this tracking. It 31717dd83bcSMatthew Dillon * must be initialized to key_beg but then is persistently updated by 31817dd83bcSMatthew Dillon * the HAMMER VFS on each successive ioctl() call. If you blow up this 31917dd83bcSMatthew Dillon * field you will blow up the mirror target, possibly to the point of 32017dd83bcSMatthew Dillon * deleting everything. As a safety measure the HAMMER VFS simply marks 32117dd83bcSMatthew Dillon * the records that the source has destroyed as deleted on the target, 32217dd83bcSMatthew Dillon * and normal pruning operations will deal with their final disposition 32317dd83bcSMatthew Dillon * at some later time. 32417dd83bcSMatthew Dillon */ 325a7fbbf91SMatthew Dillon void 326a7fbbf91SMatthew Dillon hammer_cmd_mirror_write(char **av, int ac) 327a7fbbf91SMatthew Dillon { 328a7fbbf91SMatthew Dillon struct hammer_ioc_mirror_rw mirror; 329a7fbbf91SMatthew Dillon const char *filesystem; 330a7fbbf91SMatthew Dillon char *buf = malloc(SERIALBUF_SIZE); 331d4e5b69bSMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 33217dd83bcSMatthew Dillon struct hammer_ioc_mrecord_head pickup; 333243ca327SMatthew Dillon struct hammer_ioc_synctid synctid; 33417dd83bcSMatthew Dillon union hammer_ioc_mrecord_any mrec_tmp; 33517dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 336243ca327SMatthew Dillon int error; 337243ca327SMatthew Dillon int fd; 338*48eadef9SMatthew Dillon int n; 339a7fbbf91SMatthew Dillon 340a7fbbf91SMatthew Dillon if (ac > 2) 341a7fbbf91SMatthew Dillon mirror_usage(1); 342a7fbbf91SMatthew Dillon filesystem = av[0]; 343a7fbbf91SMatthew Dillon 344*48eadef9SMatthew Dillon pickup.signature = 0; 345*48eadef9SMatthew Dillon pickup.type = 0; 346*48eadef9SMatthew Dillon 347*48eadef9SMatthew Dillon again: 348a7fbbf91SMatthew Dillon bzero(&mirror, sizeof(mirror)); 349a7fbbf91SMatthew Dillon hammer_key_beg_init(&mirror.key_beg); 350a7fbbf91SMatthew Dillon hammer_key_end_init(&mirror.key_end); 35117dd83bcSMatthew Dillon mirror.key_end = mirror.key_beg; 352a7fbbf91SMatthew Dillon 353d4e5b69bSMatthew Dillon fd = getpfs(&pfs, filesystem); 354a7fbbf91SMatthew Dillon 355243ca327SMatthew Dillon /* 356d4e5b69bSMatthew Dillon * In two-way mode the target writes out a PFS packet first. 357d4e5b69bSMatthew Dillon * The source uses our tid_end as its tid_beg by default, 358d4e5b69bSMatthew Dillon * picking up where it left off. 359243ca327SMatthew Dillon */ 360d4e5b69bSMatthew Dillon mirror.tid_beg = 0; 361d4e5b69bSMatthew Dillon if (TwoWayPipeOpt) { 362d4e5b69bSMatthew Dillon generate_mrec_header(fd, 1, pfs.pfs_id, 363d4e5b69bSMatthew Dillon &mirror.tid_beg, &mirror.tid_end); 364d4e5b69bSMatthew Dillon } 365d4e5b69bSMatthew Dillon 366d4e5b69bSMatthew Dillon /* 36717dd83bcSMatthew Dillon * Read and process the PFS header. The source informs us of 36817dd83bcSMatthew Dillon * the TID range the stream represents. 369d4e5b69bSMatthew Dillon */ 370*48eadef9SMatthew Dillon n = validate_mrec_header(fd, 0, 1, pfs.pfs_id, &pickup, 371d4e5b69bSMatthew Dillon &mirror.tid_beg, &mirror.tid_end); 372*48eadef9SMatthew Dillon if (n < 0) { /* got TERM record */ 373*48eadef9SMatthew Dillon relpfs(fd, &pfs); 374*48eadef9SMatthew Dillon return; 375*48eadef9SMatthew Dillon } 37634ebae70SMatthew Dillon 377a7fbbf91SMatthew Dillon mirror.ubuf = buf; 378a7fbbf91SMatthew Dillon mirror.size = SERIALBUF_SIZE; 379a7fbbf91SMatthew Dillon 380243ca327SMatthew Dillon /* 38117dd83bcSMatthew Dillon * Read and process bulk records (REC, PASS, and SKIP types). 38217dd83bcSMatthew Dillon * 38317dd83bcSMatthew Dillon * On your life, do NOT mess with mirror.key_cur or your mirror 38417dd83bcSMatthew Dillon * target may become history. 385243ca327SMatthew Dillon */ 386a7fbbf91SMatthew Dillon for (;;) { 387a7fbbf91SMatthew Dillon mirror.count = 0; 388d4e5b69bSMatthew Dillon mirror.pfs_id = pfs.pfs_id; 389d4e5b69bSMatthew Dillon mirror.shared_uuid = pfs.ondisk->shared_uuid; 390a7fbbf91SMatthew Dillon mirror.size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup); 391a7fbbf91SMatthew Dillon if (mirror.size <= 0) 392a7fbbf91SMatthew Dillon break; 393a7fbbf91SMatthew Dillon if (ioctl(fd, HAMMERIOC_MIRROR_WRITE, &mirror) < 0) { 394a7fbbf91SMatthew Dillon fprintf(stderr, "Mirror-write %s failed: %s\n", 395a7fbbf91SMatthew Dillon filesystem, strerror(errno)); 396a7fbbf91SMatthew Dillon exit(1); 397a7fbbf91SMatthew Dillon } 39817dd83bcSMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) { 39917dd83bcSMatthew Dillon fprintf(stderr, 40017dd83bcSMatthew Dillon "Mirror-write %s fatal error %d\n", 40117dd83bcSMatthew Dillon filesystem, mirror.head.error); 40217dd83bcSMatthew Dillon exit(1); 40317dd83bcSMatthew Dillon } 404243ca327SMatthew Dillon #if 0 405a7fbbf91SMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_INTR) { 406a7fbbf91SMatthew Dillon fprintf(stderr, 407a7fbbf91SMatthew Dillon "Mirror-write %s interrupted by timer at" 408243ca327SMatthew Dillon " %016llx\n", 409a7fbbf91SMatthew Dillon filesystem, 410243ca327SMatthew Dillon mirror.key_cur.obj_id); 411a7fbbf91SMatthew Dillon exit(0); 412a7fbbf91SMatthew Dillon } 413243ca327SMatthew Dillon #endif 414a7fbbf91SMatthew Dillon } 415243ca327SMatthew Dillon 416243ca327SMatthew Dillon /* 417243ca327SMatthew Dillon * Read and process the termination sync record. 418243ca327SMatthew Dillon */ 419243ca327SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 4209dc76cb1SMatthew Dillon 4219dc76cb1SMatthew Dillon if (mrec && mrec->head.type == HAMMER_MREC_TYPE_TERM) { 422*48eadef9SMatthew Dillon fprintf(stderr, "Mirror-write: received termination request\n"); 423*48eadef9SMatthew Dillon free(mrec); 4249dc76cb1SMatthew Dillon return; 4259dc76cb1SMatthew Dillon } 4269dc76cb1SMatthew Dillon 42717dd83bcSMatthew Dillon if (mrec == NULL || 428*48eadef9SMatthew Dillon (mrec->head.type != HAMMER_MREC_TYPE_SYNC && 429*48eadef9SMatthew Dillon mrec->head.type != HAMMER_MREC_TYPE_IDLE) || 43017dd83bcSMatthew Dillon mrec->head.rec_size != sizeof(mrec->sync)) { 431243ca327SMatthew Dillon fprintf(stderr, "Mirror-write %s: Did not get termination " 43217dd83bcSMatthew Dillon "sync record, or rec_size is wrong rt=%d\n", 43317dd83bcSMatthew Dillon filesystem, mrec->head.type); 434da44aa75SMatthew Dillon exit(1); 435243ca327SMatthew Dillon } 436243ca327SMatthew Dillon 437243ca327SMatthew Dillon /* 438243ca327SMatthew Dillon * Update the PFS info on the target so the user has visibility 439*48eadef9SMatthew Dillon * into the new snapshot, and sync the target filesystem. 440243ca327SMatthew Dillon */ 441*48eadef9SMatthew Dillon if (mrec->head.type == HAMMER_MREC_TYPE_SYNC) { 442d4e5b69bSMatthew Dillon update_pfs_snapshot(fd, mirror.tid_end, pfs.pfs_id); 443243ca327SMatthew Dillon 444243ca327SMatthew Dillon bzero(&synctid, sizeof(synctid)); 445243ca327SMatthew Dillon synctid.op = HAMMER_SYNCTID_SYNC2; 446243ca327SMatthew Dillon ioctl(fd, HAMMERIOC_SYNCTID, &synctid); 447243ca327SMatthew Dillon 448*48eadef9SMatthew Dillon if (VerboseOpt >= 2) { 449*48eadef9SMatthew Dillon fprintf(stderr, "Mirror-write %s: succeeded\n", 450*48eadef9SMatthew Dillon filesystem); 451*48eadef9SMatthew Dillon } 452*48eadef9SMatthew Dillon } 453*48eadef9SMatthew Dillon 454*48eadef9SMatthew Dillon free(mrec); 455*48eadef9SMatthew Dillon mrec = NULL; 456243ca327SMatthew Dillon 457243ca327SMatthew Dillon /* 458243ca327SMatthew Dillon * Report back to the originator. 459243ca327SMatthew Dillon */ 460243ca327SMatthew Dillon if (TwoWayPipeOpt) { 46117dd83bcSMatthew Dillon mrec_tmp.update.tid = mirror.tid_end; 462243ca327SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_UPDATE, 46317dd83bcSMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.update)); 464243ca327SMatthew Dillon } else { 465243ca327SMatthew Dillon printf("Source can update synctid to 0x%016llx\n", 466243ca327SMatthew Dillon mirror.tid_end); 467243ca327SMatthew Dillon } 468*48eadef9SMatthew Dillon relpfs(fd, &pfs); 469*48eadef9SMatthew Dillon goto again; 470243ca327SMatthew Dillon } 471243ca327SMatthew Dillon 472243ca327SMatthew Dillon void 473243ca327SMatthew Dillon hammer_cmd_mirror_dump(void) 474243ca327SMatthew Dillon { 475243ca327SMatthew Dillon char *buf = malloc(SERIALBUF_SIZE); 47617dd83bcSMatthew Dillon struct hammer_ioc_mrecord_head pickup; 47717dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 478243ca327SMatthew Dillon int error; 479243ca327SMatthew Dillon int size; 48017dd83bcSMatthew Dillon int offset; 48117dd83bcSMatthew Dillon int bytes; 482243ca327SMatthew Dillon 483243ca327SMatthew Dillon /* 484243ca327SMatthew Dillon * Read and process the PFS header 485243ca327SMatthew Dillon */ 486243ca327SMatthew Dillon pickup.signature = 0; 487243ca327SMatthew Dillon pickup.type = 0; 488243ca327SMatthew Dillon 489243ca327SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 490243ca327SMatthew Dillon 491243ca327SMatthew Dillon /* 492243ca327SMatthew Dillon * Read and process bulk records 493243ca327SMatthew Dillon */ 494243ca327SMatthew Dillon for (;;) { 495243ca327SMatthew Dillon size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup); 496243ca327SMatthew Dillon if (size <= 0) 497243ca327SMatthew Dillon break; 49817dd83bcSMatthew Dillon offset = 0; 49917dd83bcSMatthew Dillon while (offset < size) { 50017dd83bcSMatthew Dillon mrec = (void *)((char *)buf + offset); 50117dd83bcSMatthew Dillon bytes = HAMMER_HEAD_DOALIGN(mrec->head.rec_size); 50217dd83bcSMatthew Dillon if (offset + bytes > size) { 50317dd83bcSMatthew Dillon fprintf(stderr, "Misaligned record\n"); 50417dd83bcSMatthew Dillon exit(1); 50517dd83bcSMatthew Dillon } 50617dd83bcSMatthew Dillon 50717dd83bcSMatthew Dillon switch(mrec->head.type) { 50817dd83bcSMatthew Dillon case HAMMER_MREC_TYPE_REC: 509243ca327SMatthew Dillon printf("Record obj=%016llx key=%016llx " 510243ca327SMatthew Dillon "rt=%02x ot=%02x\n", 51117dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_id, 51217dd83bcSMatthew Dillon mrec->rec.leaf.base.key, 51317dd83bcSMatthew Dillon mrec->rec.leaf.base.rec_type, 51417dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_type); 515243ca327SMatthew Dillon printf(" tids %016llx:%016llx data=%d\n", 51617dd83bcSMatthew Dillon mrec->rec.leaf.base.create_tid, 51717dd83bcSMatthew Dillon mrec->rec.leaf.base.delete_tid, 51817dd83bcSMatthew Dillon mrec->rec.leaf.data_len); 51917dd83bcSMatthew Dillon break; 52017dd83bcSMatthew Dillon case HAMMER_MREC_TYPE_PASS: 52117dd83bcSMatthew Dillon printf("Pass obj=%016llx key=%016llx " 52217dd83bcSMatthew Dillon "rt=%02x ot=%02x\n", 52317dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_id, 52417dd83bcSMatthew Dillon mrec->rec.leaf.base.key, 52517dd83bcSMatthew Dillon mrec->rec.leaf.base.rec_type, 52617dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_type); 52717dd83bcSMatthew Dillon printf(" tids %016llx:%016llx data=%d\n", 52817dd83bcSMatthew Dillon mrec->rec.leaf.base.create_tid, 52917dd83bcSMatthew Dillon mrec->rec.leaf.base.delete_tid, 53017dd83bcSMatthew Dillon mrec->rec.leaf.data_len); 53117dd83bcSMatthew Dillon break; 53217dd83bcSMatthew Dillon case HAMMER_MREC_TYPE_SKIP: 53317dd83bcSMatthew Dillon printf("Skip obj=%016llx key=%016llx rt=%02x to\n" 53417dd83bcSMatthew Dillon " obj=%016llx key=%016llx rt=%02x\n", 53517dd83bcSMatthew Dillon mrec->skip.skip_beg.obj_id, 53617dd83bcSMatthew Dillon mrec->skip.skip_beg.key, 53717dd83bcSMatthew Dillon mrec->skip.skip_beg.rec_type, 53817dd83bcSMatthew Dillon mrec->skip.skip_end.obj_id, 53917dd83bcSMatthew Dillon mrec->skip.skip_end.key, 54017dd83bcSMatthew Dillon mrec->skip.skip_end.rec_type); 54117dd83bcSMatthew Dillon default: 54217dd83bcSMatthew Dillon break; 54317dd83bcSMatthew Dillon } 54417dd83bcSMatthew Dillon offset += bytes; 545243ca327SMatthew Dillon } 546243ca327SMatthew Dillon } 547243ca327SMatthew Dillon 548243ca327SMatthew Dillon /* 549243ca327SMatthew Dillon * Read and process the termination sync record. 550243ca327SMatthew Dillon */ 551243ca327SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 552*48eadef9SMatthew Dillon if (mrec == NULL || 553*48eadef9SMatthew Dillon (mrec->head.type != HAMMER_MREC_TYPE_SYNC && 554*48eadef9SMatthew Dillon mrec->head.type != HAMMER_MREC_TYPE_IDLE) 555*48eadef9SMatthew Dillon ) { 556243ca327SMatthew Dillon fprintf(stderr, "Mirror-dump: Did not get termination " 557243ca327SMatthew Dillon "sync record\n"); 558243ca327SMatthew Dillon } 559a7fbbf91SMatthew Dillon } 560a7fbbf91SMatthew Dillon 561a7fbbf91SMatthew Dillon void 562*48eadef9SMatthew Dillon hammer_cmd_mirror_copy(char **av, int ac, int streaming) 563a7fbbf91SMatthew Dillon { 56434ebae70SMatthew Dillon pid_t pid1; 56534ebae70SMatthew Dillon pid_t pid2; 56634ebae70SMatthew Dillon int fds[2]; 567243ca327SMatthew Dillon const char *xav[16]; 568243ca327SMatthew Dillon char tbuf[16]; 56934ebae70SMatthew Dillon char *ptr; 570243ca327SMatthew Dillon int xac; 57134ebae70SMatthew Dillon 57234ebae70SMatthew Dillon if (ac != 2) 57334ebae70SMatthew Dillon mirror_usage(1); 57434ebae70SMatthew Dillon 57534ebae70SMatthew Dillon if (pipe(fds) < 0) { 57634ebae70SMatthew Dillon perror("pipe"); 57734ebae70SMatthew Dillon exit(1); 57834ebae70SMatthew Dillon } 57934ebae70SMatthew Dillon 580243ca327SMatthew Dillon TwoWayPipeOpt = 1; 581243ca327SMatthew Dillon 58234ebae70SMatthew Dillon /* 58334ebae70SMatthew Dillon * Source 58434ebae70SMatthew Dillon */ 58534ebae70SMatthew Dillon if ((pid1 = fork()) == 0) { 58634ebae70SMatthew Dillon dup2(fds[0], 0); 58734ebae70SMatthew Dillon dup2(fds[0], 1); 58834ebae70SMatthew Dillon close(fds[0]); 58934ebae70SMatthew Dillon close(fds[1]); 59034ebae70SMatthew Dillon if ((ptr = strchr(av[0], ':')) != NULL) { 59134ebae70SMatthew Dillon *ptr++ = 0; 592243ca327SMatthew Dillon xac = 0; 593243ca327SMatthew Dillon xav[xac++] = "ssh"; 594243ca327SMatthew Dillon xav[xac++] = av[0]; 595243ca327SMatthew Dillon xav[xac++] = "hammer"; 596*48eadef9SMatthew Dillon 597*48eadef9SMatthew Dillon switch(VerboseOpt) { 598*48eadef9SMatthew Dillon case 0: 599*48eadef9SMatthew Dillon break; 600*48eadef9SMatthew Dillon case 1: 601243ca327SMatthew Dillon xav[xac++] = "-v"; 602*48eadef9SMatthew Dillon break; 603*48eadef9SMatthew Dillon case 2: 604*48eadef9SMatthew Dillon xav[xac++] = "-vv"; 605*48eadef9SMatthew Dillon break; 606*48eadef9SMatthew Dillon default: 607*48eadef9SMatthew Dillon xav[xac++] = "-vvv"; 608*48eadef9SMatthew Dillon break; 609*48eadef9SMatthew Dillon } 610243ca327SMatthew Dillon xav[xac++] = "-2"; 611243ca327SMatthew Dillon if (TimeoutOpt) { 612243ca327SMatthew Dillon snprintf(tbuf, sizeof(tbuf), "%d", TimeoutOpt); 613243ca327SMatthew Dillon xav[xac++] = "-t"; 614243ca327SMatthew Dillon xav[xac++] = tbuf; 615243ca327SMatthew Dillon } 616*48eadef9SMatthew Dillon if (streaming) 617*48eadef9SMatthew Dillon xav[xac++] = "mirror-read-streaming"; 618*48eadef9SMatthew Dillon else 619243ca327SMatthew Dillon xav[xac++] = "mirror-read"; 620243ca327SMatthew Dillon xav[xac++] = ptr; 621243ca327SMatthew Dillon xav[xac++] = NULL; 622243ca327SMatthew Dillon execv("/usr/bin/ssh", (void *)xav); 62334ebae70SMatthew Dillon } else { 624*48eadef9SMatthew Dillon hammer_cmd_mirror_read(av, 1, streaming); 625243ca327SMatthew Dillon fflush(stdout); 626243ca327SMatthew Dillon fflush(stderr); 62734ebae70SMatthew Dillon } 62853d93cc7SMatthew Dillon _exit(1); 62934ebae70SMatthew Dillon } 63034ebae70SMatthew Dillon 63134ebae70SMatthew Dillon /* 63234ebae70SMatthew Dillon * Target 63334ebae70SMatthew Dillon */ 63434ebae70SMatthew Dillon if ((pid2 = fork()) == 0) { 63534ebae70SMatthew Dillon dup2(fds[1], 0); 63634ebae70SMatthew Dillon dup2(fds[1], 1); 63734ebae70SMatthew Dillon close(fds[0]); 63834ebae70SMatthew Dillon close(fds[1]); 63934ebae70SMatthew Dillon if ((ptr = strchr(av[1], ':')) != NULL) { 64034ebae70SMatthew Dillon *ptr++ = 0; 641243ca327SMatthew Dillon xac = 0; 642243ca327SMatthew Dillon xav[xac++] = "ssh"; 643243ca327SMatthew Dillon xav[xac++] = av[1]; 644243ca327SMatthew Dillon xav[xac++] = "hammer"; 645*48eadef9SMatthew Dillon 646*48eadef9SMatthew Dillon switch(VerboseOpt) { 647*48eadef9SMatthew Dillon case 0: 648*48eadef9SMatthew Dillon break; 649*48eadef9SMatthew Dillon case 1: 650243ca327SMatthew Dillon xav[xac++] = "-v"; 651*48eadef9SMatthew Dillon break; 652*48eadef9SMatthew Dillon case 2: 653*48eadef9SMatthew Dillon xav[xac++] = "-vv"; 654*48eadef9SMatthew Dillon break; 655*48eadef9SMatthew Dillon default: 656*48eadef9SMatthew Dillon xav[xac++] = "-vvv"; 657*48eadef9SMatthew Dillon break; 658*48eadef9SMatthew Dillon } 659*48eadef9SMatthew Dillon 660243ca327SMatthew Dillon xav[xac++] = "-2"; 661243ca327SMatthew Dillon xav[xac++] = "mirror-write"; 662243ca327SMatthew Dillon xav[xac++] = ptr; 663243ca327SMatthew Dillon xav[xac++] = NULL; 664243ca327SMatthew Dillon execv("/usr/bin/ssh", (void *)xav); 66534ebae70SMatthew Dillon } else { 66634ebae70SMatthew Dillon hammer_cmd_mirror_write(av + 1, 1); 667243ca327SMatthew Dillon fflush(stdout); 668243ca327SMatthew Dillon fflush(stderr); 66934ebae70SMatthew Dillon } 67053d93cc7SMatthew Dillon _exit(1); 67134ebae70SMatthew Dillon } 67234ebae70SMatthew Dillon close(fds[0]); 67334ebae70SMatthew Dillon close(fds[1]); 67434ebae70SMatthew Dillon 67534ebae70SMatthew Dillon while (waitpid(pid1, NULL, 0) <= 0) 67634ebae70SMatthew Dillon ; 67734ebae70SMatthew Dillon while (waitpid(pid2, NULL, 0) <= 0) 67834ebae70SMatthew Dillon ; 679a7fbbf91SMatthew Dillon } 680a7fbbf91SMatthew Dillon 681243ca327SMatthew Dillon /* 682243ca327SMatthew Dillon * Read and return multiple mrecords 683243ca327SMatthew Dillon */ 684a7fbbf91SMatthew Dillon static int 68517dd83bcSMatthew Dillon read_mrecords(int fd, char *buf, u_int size, hammer_ioc_mrecord_head_t pickup) 686a7fbbf91SMatthew Dillon { 68717dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 688a7fbbf91SMatthew Dillon u_int count; 689a7fbbf91SMatthew Dillon size_t n; 690a7fbbf91SMatthew Dillon size_t i; 69117dd83bcSMatthew Dillon size_t bytes; 692a7fbbf91SMatthew Dillon 693a7fbbf91SMatthew Dillon count = 0; 694a7fbbf91SMatthew Dillon while (size - count >= HAMMER_MREC_HEADSIZE) { 695a7fbbf91SMatthew Dillon /* 696a7fbbf91SMatthew Dillon * Cached the record header in case we run out of buffer 697a7fbbf91SMatthew Dillon * space. 698a7fbbf91SMatthew Dillon */ 69917dd83bcSMatthew Dillon fflush(stdout); 700a7fbbf91SMatthew Dillon if (pickup->signature == 0) { 701a7fbbf91SMatthew Dillon for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) { 702a7fbbf91SMatthew Dillon i = read(fd, (char *)pickup + n, 703a7fbbf91SMatthew Dillon HAMMER_MREC_HEADSIZE - n); 704a7fbbf91SMatthew Dillon if (i <= 0) 705a7fbbf91SMatthew Dillon break; 706a7fbbf91SMatthew Dillon } 707a7fbbf91SMatthew Dillon if (n == 0) 708a7fbbf91SMatthew Dillon break; 709a7fbbf91SMatthew Dillon if (n != HAMMER_MREC_HEADSIZE) { 710a7fbbf91SMatthew Dillon fprintf(stderr, "read_mrecords: short read on pipe\n"); 711a7fbbf91SMatthew Dillon exit(1); 712a7fbbf91SMatthew Dillon } 713a7fbbf91SMatthew Dillon 714a7fbbf91SMatthew Dillon if (pickup->signature != HAMMER_IOC_MIRROR_SIGNATURE) { 715a7fbbf91SMatthew Dillon fprintf(stderr, "read_mrecords: malformed record on pipe, bad signature\n"); 716a7fbbf91SMatthew Dillon exit(1); 717a7fbbf91SMatthew Dillon } 718a7fbbf91SMatthew Dillon } 719a7fbbf91SMatthew Dillon if (pickup->rec_size < HAMMER_MREC_HEADSIZE || 72017dd83bcSMatthew Dillon pickup->rec_size > sizeof(*mrec) + HAMMER_XBUFSIZE) { 721a7fbbf91SMatthew Dillon fprintf(stderr, "read_mrecords: malformed record on pipe, illegal rec_size\n"); 722a7fbbf91SMatthew Dillon exit(1); 723a7fbbf91SMatthew Dillon } 724a7fbbf91SMatthew Dillon 725a7fbbf91SMatthew Dillon /* 726a7fbbf91SMatthew Dillon * Stop if we have insufficient space for the record and data. 727a7fbbf91SMatthew Dillon */ 72817dd83bcSMatthew Dillon bytes = HAMMER_HEAD_DOALIGN(pickup->rec_size); 72917dd83bcSMatthew Dillon if (size - count < bytes) 730a7fbbf91SMatthew Dillon break; 731a7fbbf91SMatthew Dillon 732a7fbbf91SMatthew Dillon /* 73317dd83bcSMatthew Dillon * Stop if the record type is not a REC or a SKIP (the only 73417dd83bcSMatthew Dillon * two types the ioctl supports. Other types are used only 73517dd83bcSMatthew Dillon * by the userland protocol). 736243ca327SMatthew Dillon */ 73717dd83bcSMatthew Dillon if (pickup->type != HAMMER_MREC_TYPE_REC && 73817dd83bcSMatthew Dillon pickup->type != HAMMER_MREC_TYPE_SKIP && 73917dd83bcSMatthew Dillon pickup->type != HAMMER_MREC_TYPE_PASS) { 740243ca327SMatthew Dillon break; 74117dd83bcSMatthew Dillon } 742243ca327SMatthew Dillon 743243ca327SMatthew Dillon /* 744a7fbbf91SMatthew Dillon * Read the remainder and clear the pickup signature. 745a7fbbf91SMatthew Dillon */ 74617dd83bcSMatthew Dillon for (n = HAMMER_MREC_HEADSIZE; n < bytes; n += i) { 74717dd83bcSMatthew Dillon i = read(fd, buf + count + n, bytes - n); 748a7fbbf91SMatthew Dillon if (i <= 0) 749a7fbbf91SMatthew Dillon break; 750a7fbbf91SMatthew Dillon } 75117dd83bcSMatthew Dillon if (n != bytes) { 752a7fbbf91SMatthew Dillon fprintf(stderr, "read_mrecords: short read on pipe\n"); 753a7fbbf91SMatthew Dillon exit(1); 754a7fbbf91SMatthew Dillon } 75517dd83bcSMatthew Dillon 75617dd83bcSMatthew Dillon bcopy(pickup, buf + count, HAMMER_MREC_HEADSIZE); 75717dd83bcSMatthew Dillon pickup->signature = 0; 75817dd83bcSMatthew Dillon pickup->type = 0; 75917dd83bcSMatthew Dillon mrec = (void *)(buf + count); 76017dd83bcSMatthew Dillon 76117dd83bcSMatthew Dillon /* 76217dd83bcSMatthew Dillon * Validate the completed record 76317dd83bcSMatthew Dillon */ 76417dd83bcSMatthew Dillon if (mrec->head.rec_crc != 76517dd83bcSMatthew Dillon crc32((char *)mrec + HAMMER_MREC_CRCOFF, 76617dd83bcSMatthew Dillon mrec->head.rec_size - HAMMER_MREC_CRCOFF)) { 76717dd83bcSMatthew Dillon fprintf(stderr, "read_mrecords: malformed record " 76817dd83bcSMatthew Dillon "on pipe, bad crc\n"); 76917dd83bcSMatthew Dillon exit(1); 770a7fbbf91SMatthew Dillon } 771a7fbbf91SMatthew Dillon 77217dd83bcSMatthew Dillon /* 77317dd83bcSMatthew Dillon * If its a B-Tree record validate the data crc 77417dd83bcSMatthew Dillon */ 77517dd83bcSMatthew Dillon if (mrec->head.type == HAMMER_MREC_TYPE_REC) { 77617dd83bcSMatthew Dillon if (mrec->head.rec_size < 77717dd83bcSMatthew Dillon sizeof(mrec->rec) + mrec->rec.leaf.data_len) { 77817dd83bcSMatthew Dillon fprintf(stderr, 77917dd83bcSMatthew Dillon "read_mrecords: malformed record on " 78017dd83bcSMatthew Dillon "pipe, illegal element data_len\n"); 78117dd83bcSMatthew Dillon exit(1); 78217dd83bcSMatthew Dillon } 78317dd83bcSMatthew Dillon if (mrec->rec.leaf.data_len && 78417dd83bcSMatthew Dillon mrec->rec.leaf.data_offset && 78517dd83bcSMatthew Dillon hammer_crc_test_leaf(&mrec->rec + 1, &mrec->rec.leaf) == 0) { 78617dd83bcSMatthew Dillon fprintf(stderr, 78717dd83bcSMatthew Dillon "read_mrecords: data_crc did not " 78817dd83bcSMatthew Dillon "match data! obj=%016llx key=%016llx\n", 78917dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_id, 79017dd83bcSMatthew Dillon mrec->rec.leaf.base.key); 79117dd83bcSMatthew Dillon fprintf(stderr, 79217dd83bcSMatthew Dillon "continuing, but there are problems\n"); 79317dd83bcSMatthew Dillon } 79417dd83bcSMatthew Dillon } 79517dd83bcSMatthew Dillon count += bytes; 796a7fbbf91SMatthew Dillon } 797a7fbbf91SMatthew Dillon return(count); 798a7fbbf91SMatthew Dillon } 799a7fbbf91SMatthew Dillon 80034ebae70SMatthew Dillon /* 80117dd83bcSMatthew Dillon * Read and return a single mrecord. 802243ca327SMatthew Dillon */ 803243ca327SMatthew Dillon static 80417dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t 80517dd83bcSMatthew Dillon read_mrecord(int fdin, int *errorp, hammer_ioc_mrecord_head_t pickup) 806243ca327SMatthew Dillon { 80717dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 80817dd83bcSMatthew Dillon struct hammer_ioc_mrecord_head mrechd; 809243ca327SMatthew Dillon size_t bytes; 810243ca327SMatthew Dillon size_t n; 811243ca327SMatthew Dillon size_t i; 812243ca327SMatthew Dillon 813243ca327SMatthew Dillon if (pickup && pickup->type != 0) { 814243ca327SMatthew Dillon mrechd = *pickup; 815243ca327SMatthew Dillon pickup->signature = 0; 816243ca327SMatthew Dillon pickup->type = 0; 817243ca327SMatthew Dillon n = HAMMER_MREC_HEADSIZE; 818243ca327SMatthew Dillon } else { 819243ca327SMatthew Dillon /* 820243ca327SMatthew Dillon * Read in the PFSD header from the sender. 821243ca327SMatthew Dillon */ 822243ca327SMatthew Dillon for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) { 823243ca327SMatthew Dillon i = read(fdin, (char *)&mrechd + n, HAMMER_MREC_HEADSIZE - n); 824243ca327SMatthew Dillon if (i <= 0) 825243ca327SMatthew Dillon break; 826243ca327SMatthew Dillon } 827243ca327SMatthew Dillon if (n == 0) { 828243ca327SMatthew Dillon *errorp = 0; /* EOF */ 829243ca327SMatthew Dillon return(NULL); 830243ca327SMatthew Dillon } 831243ca327SMatthew Dillon if (n != HAMMER_MREC_HEADSIZE) { 832243ca327SMatthew Dillon fprintf(stderr, "short read of mrecord header\n"); 833243ca327SMatthew Dillon *errorp = EPIPE; 834243ca327SMatthew Dillon return(NULL); 835243ca327SMatthew Dillon } 836243ca327SMatthew Dillon } 837243ca327SMatthew Dillon if (mrechd.signature != HAMMER_IOC_MIRROR_SIGNATURE) { 838243ca327SMatthew Dillon fprintf(stderr, "read_mrecord: bad signature\n"); 839243ca327SMatthew Dillon *errorp = EINVAL; 840243ca327SMatthew Dillon return(NULL); 841243ca327SMatthew Dillon } 84217dd83bcSMatthew Dillon bytes = HAMMER_HEAD_DOALIGN(mrechd.rec_size); 84317dd83bcSMatthew Dillon assert(bytes >= sizeof(mrechd)); 844243ca327SMatthew Dillon mrec = malloc(bytes); 84517dd83bcSMatthew Dillon mrec->head = mrechd; 84617dd83bcSMatthew Dillon 847243ca327SMatthew Dillon while (n < bytes) { 848243ca327SMatthew Dillon i = read(fdin, (char *)mrec + n, bytes - n); 849243ca327SMatthew Dillon if (i <= 0) 850243ca327SMatthew Dillon break; 851243ca327SMatthew Dillon n += i; 852243ca327SMatthew Dillon } 853243ca327SMatthew Dillon if (n != bytes) { 854243ca327SMatthew Dillon fprintf(stderr, "read_mrecord: short read on payload\n"); 855243ca327SMatthew Dillon *errorp = EPIPE; 856243ca327SMatthew Dillon return(NULL); 857243ca327SMatthew Dillon } 85817dd83bcSMatthew Dillon if (mrec->head.rec_crc != 85917dd83bcSMatthew Dillon crc32((char *)mrec + HAMMER_MREC_CRCOFF, 86017dd83bcSMatthew Dillon mrec->head.rec_size - HAMMER_MREC_CRCOFF)) { 861243ca327SMatthew Dillon fprintf(stderr, "read_mrecord: bad CRC\n"); 862243ca327SMatthew Dillon *errorp = EINVAL; 863243ca327SMatthew Dillon return(NULL); 864243ca327SMatthew Dillon } 865243ca327SMatthew Dillon *errorp = 0; 866243ca327SMatthew Dillon return(mrec); 867243ca327SMatthew Dillon } 868243ca327SMatthew Dillon 869243ca327SMatthew Dillon static 870243ca327SMatthew Dillon void 87117dd83bcSMatthew Dillon write_mrecord(int fdout, u_int32_t type, hammer_ioc_mrecord_any_t mrec, 87217dd83bcSMatthew Dillon int bytes) 873243ca327SMatthew Dillon { 87417dd83bcSMatthew Dillon char zbuf[HAMMER_HEAD_ALIGN]; 87517dd83bcSMatthew Dillon int pad; 876243ca327SMatthew Dillon 87717dd83bcSMatthew Dillon pad = HAMMER_HEAD_DOALIGN(bytes) - bytes; 87817dd83bcSMatthew Dillon 87917dd83bcSMatthew Dillon assert(bytes >= (int)sizeof(mrec->head)); 88017dd83bcSMatthew Dillon bzero(&mrec->head, sizeof(mrec->head)); 88117dd83bcSMatthew Dillon mrec->head.signature = HAMMER_IOC_MIRROR_SIGNATURE; 88217dd83bcSMatthew Dillon mrec->head.type = type; 88317dd83bcSMatthew Dillon mrec->head.rec_size = bytes; 88417dd83bcSMatthew Dillon mrec->head.rec_crc = crc32((char *)mrec + HAMMER_MREC_CRCOFF, 88517dd83bcSMatthew Dillon bytes - HAMMER_MREC_CRCOFF); 88617dd83bcSMatthew Dillon if (write(fdout, mrec, bytes) != bytes) { 887243ca327SMatthew Dillon fprintf(stderr, "write_mrecord: error %d (%s)\n", 888243ca327SMatthew Dillon errno, strerror(errno)); 889243ca327SMatthew Dillon exit(1); 890243ca327SMatthew Dillon } 89117dd83bcSMatthew Dillon if (pad) { 89217dd83bcSMatthew Dillon bzero(zbuf, pad); 89317dd83bcSMatthew Dillon if (write(fdout, zbuf, pad) != pad) { 89417dd83bcSMatthew Dillon fprintf(stderr, "write_mrecord: error %d (%s)\n", 89517dd83bcSMatthew Dillon errno, strerror(errno)); 89617dd83bcSMatthew Dillon exit(1); 89717dd83bcSMatthew Dillon } 89817dd83bcSMatthew Dillon } 899243ca327SMatthew Dillon } 900243ca327SMatthew Dillon 901243ca327SMatthew Dillon /* 90234ebae70SMatthew Dillon * Generate a mirroring header with the pfs information of the 90334ebae70SMatthew Dillon * originating filesytem. 90434ebae70SMatthew Dillon */ 90534ebae70SMatthew Dillon static void 906d4e5b69bSMatthew Dillon generate_mrec_header(int fd, int fdout, int pfs_id, 90734ebae70SMatthew Dillon hammer_tid_t *tid_begp, hammer_tid_t *tid_endp) 90834ebae70SMatthew Dillon { 90934ebae70SMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 91017dd83bcSMatthew Dillon union hammer_ioc_mrecord_any mrec_tmp; 91134ebae70SMatthew Dillon 91234ebae70SMatthew Dillon bzero(&pfs, sizeof(pfs)); 91317dd83bcSMatthew Dillon bzero(&mrec_tmp, sizeof(mrec_tmp)); 914d4e5b69bSMatthew Dillon pfs.pfs_id = pfs_id; 91517dd83bcSMatthew Dillon pfs.ondisk = &mrec_tmp.pfs.pfsd; 91617dd83bcSMatthew Dillon pfs.bytes = sizeof(mrec_tmp.pfs.pfsd); 91734ebae70SMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 9189dc76cb1SMatthew Dillon fprintf(stderr, "Mirror-read: not a HAMMER fs/pseudofs!\n"); 91934ebae70SMatthew Dillon exit(1); 92034ebae70SMatthew Dillon } 92134ebae70SMatthew Dillon if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) { 9229dc76cb1SMatthew Dillon fprintf(stderr, "Mirror-read: HAMMER pfs version mismatch!\n"); 92334ebae70SMatthew Dillon exit(1); 92434ebae70SMatthew Dillon } 92534ebae70SMatthew Dillon 92634ebae70SMatthew Dillon /* 92734ebae70SMatthew Dillon * sync_beg_tid - lowest TID on source after which a full history 92834ebae70SMatthew Dillon * is available. 92934ebae70SMatthew Dillon * 93034ebae70SMatthew Dillon * sync_end_tid - highest fully synchronized TID from source. 93134ebae70SMatthew Dillon */ 93217dd83bcSMatthew Dillon if (tid_begp && *tid_begp < mrec_tmp.pfs.pfsd.sync_beg_tid) 93317dd83bcSMatthew Dillon *tid_begp = mrec_tmp.pfs.pfsd.sync_beg_tid; 934d4e5b69bSMatthew Dillon if (tid_endp) 93517dd83bcSMatthew Dillon *tid_endp = mrec_tmp.pfs.pfsd.sync_end_tid; 93617dd83bcSMatthew Dillon mrec_tmp.pfs.version = pfs.version; 937243ca327SMatthew Dillon write_mrecord(fdout, HAMMER_MREC_TYPE_PFSD, 93817dd83bcSMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.pfs)); 93934ebae70SMatthew Dillon } 94034ebae70SMatthew Dillon 94134ebae70SMatthew Dillon /* 94234ebae70SMatthew Dillon * Validate the pfs information from the originating filesystem 94334ebae70SMatthew Dillon * against the target filesystem. shared_uuid must match. 944*48eadef9SMatthew Dillon * 945*48eadef9SMatthew Dillon * return -1 if we got a TERM record 94634ebae70SMatthew Dillon */ 947*48eadef9SMatthew Dillon static int 948d4e5b69bSMatthew Dillon validate_mrec_header(int fd, int fdin, int is_target, int pfs_id, 949*48eadef9SMatthew Dillon struct hammer_ioc_mrecord_head *pickup, 95034ebae70SMatthew Dillon hammer_tid_t *tid_begp, hammer_tid_t *tid_endp) 95134ebae70SMatthew Dillon { 95234ebae70SMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 95334ebae70SMatthew Dillon struct hammer_pseudofs_data pfsd; 95417dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 955243ca327SMatthew Dillon int error; 95634ebae70SMatthew Dillon 95734ebae70SMatthew Dillon /* 95834ebae70SMatthew Dillon * Get the PFSD info from the target filesystem. 95934ebae70SMatthew Dillon */ 96034ebae70SMatthew Dillon bzero(&pfs, sizeof(pfs)); 96134ebae70SMatthew Dillon bzero(&pfsd, sizeof(pfsd)); 962d4e5b69bSMatthew Dillon pfs.pfs_id = pfs_id; 96334ebae70SMatthew Dillon pfs.ondisk = &pfsd; 96434ebae70SMatthew Dillon pfs.bytes = sizeof(pfsd); 96534ebae70SMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 96634ebae70SMatthew Dillon fprintf(stderr, "mirror-write: not a HAMMER fs/pseudofs!\n"); 96734ebae70SMatthew Dillon exit(1); 96834ebae70SMatthew Dillon } 96934ebae70SMatthew Dillon if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) { 97034ebae70SMatthew Dillon fprintf(stderr, "mirror-write: HAMMER pfs version mismatch!\n"); 97134ebae70SMatthew Dillon exit(1); 97234ebae70SMatthew Dillon } 97334ebae70SMatthew Dillon 974*48eadef9SMatthew Dillon mrec = read_mrecord(fdin, &error, pickup); 975243ca327SMatthew Dillon if (mrec == NULL) { 976243ca327SMatthew Dillon if (error == 0) 977243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: short read\n"); 97834ebae70SMatthew Dillon exit(1); 97934ebae70SMatthew Dillon } 980*48eadef9SMatthew Dillon if (mrec->head.type == HAMMER_MREC_TYPE_TERM) { 981*48eadef9SMatthew Dillon free(mrec); 982*48eadef9SMatthew Dillon return(-1); 983*48eadef9SMatthew Dillon } 984*48eadef9SMatthew Dillon 98517dd83bcSMatthew Dillon if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) { 986243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: did not get expected " 987243ca327SMatthew Dillon "PFSD record type\n"); 98834ebae70SMatthew Dillon exit(1); 98934ebae70SMatthew Dillon } 99017dd83bcSMatthew Dillon if (mrec->head.rec_size != sizeof(mrec->pfs)) { 991243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: unexpected payload " 992243ca327SMatthew Dillon "size\n"); 99334ebae70SMatthew Dillon exit(1); 99434ebae70SMatthew Dillon } 99517dd83bcSMatthew Dillon if (mrec->pfs.version != pfs.version) { 996243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: Version mismatch\n"); 99734ebae70SMatthew Dillon exit(1); 99834ebae70SMatthew Dillon } 99934ebae70SMatthew Dillon 100034ebae70SMatthew Dillon /* 100134ebae70SMatthew Dillon * Whew. Ok, is the read PFS info compatible with the target? 100234ebae70SMatthew Dillon */ 100317dd83bcSMatthew Dillon if (bcmp(&mrec->pfs.pfsd.shared_uuid, &pfsd.shared_uuid, 100417dd83bcSMatthew Dillon sizeof(pfsd.shared_uuid)) != 0) { 100517dd83bcSMatthew Dillon fprintf(stderr, 100617dd83bcSMatthew Dillon "mirror-write: source and target have " 1007f265b84fSSascha Wildner "different shared-uuid's!\n"); 100834ebae70SMatthew Dillon exit(1); 100934ebae70SMatthew Dillon } 1010d4e5b69bSMatthew Dillon if (is_target && 1011d4e5b69bSMatthew Dillon (pfsd.mirror_flags & HAMMER_PFSD_SLAVE) == 0) { 101234ebae70SMatthew Dillon fprintf(stderr, "mirror-write: target must be in slave mode\n"); 101334ebae70SMatthew Dillon exit(1); 101434ebae70SMatthew Dillon } 1015d4e5b69bSMatthew Dillon if (tid_begp) 101617dd83bcSMatthew Dillon *tid_begp = mrec->pfs.pfsd.sync_beg_tid; 1017d4e5b69bSMatthew Dillon if (tid_endp) 101817dd83bcSMatthew Dillon *tid_endp = mrec->pfs.pfsd.sync_end_tid; 1019243ca327SMatthew Dillon free(mrec); 1020*48eadef9SMatthew Dillon return(0); 102134ebae70SMatthew Dillon } 102234ebae70SMatthew Dillon 102334ebae70SMatthew Dillon static void 1024d4e5b69bSMatthew Dillon update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id) 102534ebae70SMatthew Dillon { 1026243ca327SMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 1027243ca327SMatthew Dillon struct hammer_pseudofs_data pfsd; 102834ebae70SMatthew Dillon 1029243ca327SMatthew Dillon bzero(&pfs, sizeof(pfs)); 1030243ca327SMatthew Dillon bzero(&pfsd, sizeof(pfsd)); 1031d4e5b69bSMatthew Dillon pfs.pfs_id = pfs_id; 1032243ca327SMatthew Dillon pfs.ondisk = &pfsd; 1033243ca327SMatthew Dillon pfs.bytes = sizeof(pfsd); 1034243ca327SMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 1035243ca327SMatthew Dillon perror("update_pfs_snapshot (read)"); 1036243ca327SMatthew Dillon exit(1); 103734ebae70SMatthew Dillon } 10389c67b4d2SMatthew Dillon if (pfsd.sync_end_tid != snapshot_tid) { 1039ddc8e722SMatthew Dillon pfsd.sync_end_tid = snapshot_tid; 1040243ca327SMatthew Dillon if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) != 0) { 1041243ca327SMatthew Dillon perror("update_pfs_snapshot (rewrite)"); 1042243ca327SMatthew Dillon exit(1); 104334ebae70SMatthew Dillon } 1044*48eadef9SMatthew Dillon if (VerboseOpt >= 2) { 10459dc76cb1SMatthew Dillon fprintf(stderr, 10469dc76cb1SMatthew Dillon "Mirror-write: Completed, updated snapshot " 10479dc76cb1SMatthew Dillon "to %016llx\n", 10489dc76cb1SMatthew Dillon snapshot_tid); 1049243ca327SMatthew Dillon } 10509c67b4d2SMatthew Dillon } 1051*48eadef9SMatthew Dillon } 1052243ca327SMatthew Dillon 1053*48eadef9SMatthew Dillon /* 1054*48eadef9SMatthew Dillon * Bandwidth-limited write in chunks 1055*48eadef9SMatthew Dillon */ 1056*48eadef9SMatthew Dillon static 1057*48eadef9SMatthew Dillon ssize_t 1058*48eadef9SMatthew Dillon writebw(int fd, const void *buf, size_t nbytes, 1059*48eadef9SMatthew Dillon u_int64_t *bwcount, struct timeval *tv1) 1060*48eadef9SMatthew Dillon { 1061*48eadef9SMatthew Dillon struct timeval tv2; 1062*48eadef9SMatthew Dillon size_t n; 1063*48eadef9SMatthew Dillon ssize_t r; 1064*48eadef9SMatthew Dillon ssize_t a; 1065*48eadef9SMatthew Dillon int usec; 1066*48eadef9SMatthew Dillon 1067*48eadef9SMatthew Dillon a = 0; 1068*48eadef9SMatthew Dillon r = 0; 1069*48eadef9SMatthew Dillon while (nbytes) { 1070*48eadef9SMatthew Dillon if (*bwcount + nbytes > BandwidthOpt) 1071*48eadef9SMatthew Dillon n = BandwidthOpt - *bwcount; 1072*48eadef9SMatthew Dillon else 1073*48eadef9SMatthew Dillon n = nbytes; 1074*48eadef9SMatthew Dillon if (n) 1075*48eadef9SMatthew Dillon r = write(fd, buf, n); 1076*48eadef9SMatthew Dillon if (r >= 0) { 1077*48eadef9SMatthew Dillon a += r; 1078*48eadef9SMatthew Dillon nbytes -= r; 1079*48eadef9SMatthew Dillon buf = (const char *)buf + r; 1080*48eadef9SMatthew Dillon } 1081*48eadef9SMatthew Dillon if ((size_t)r != n) 1082*48eadef9SMatthew Dillon break; 1083*48eadef9SMatthew Dillon *bwcount += n; 1084*48eadef9SMatthew Dillon if (*bwcount >= BandwidthOpt) { 1085*48eadef9SMatthew Dillon gettimeofday(&tv2, NULL); 1086*48eadef9SMatthew Dillon usec = (int)(tv2.tv_sec - tv1->tv_sec) * 1000000 + 1087*48eadef9SMatthew Dillon (int)(tv2.tv_usec - tv1->tv_usec); 1088*48eadef9SMatthew Dillon if (usec >= 0 && usec < 1000000) 1089*48eadef9SMatthew Dillon usleep(1000000 - usec); 1090*48eadef9SMatthew Dillon gettimeofday(tv1, NULL); 1091*48eadef9SMatthew Dillon *bwcount -= BandwidthOpt; 1092*48eadef9SMatthew Dillon } 1093*48eadef9SMatthew Dillon } 1094*48eadef9SMatthew Dillon return(a ? a : r); 1095*48eadef9SMatthew Dillon } 109634ebae70SMatthew Dillon 1097a7fbbf91SMatthew Dillon static void 1098a7fbbf91SMatthew Dillon mirror_usage(int code) 1099a7fbbf91SMatthew Dillon { 1100a7fbbf91SMatthew Dillon fprintf(stderr, 1101a7fbbf91SMatthew Dillon "hammer mirror-read <filesystem>\n" 1102a7fbbf91SMatthew Dillon "hammer mirror-write <filesystem>\n" 1103243ca327SMatthew Dillon "hammer mirror-dump\n" 1104a7fbbf91SMatthew Dillon "hammer mirror-copy [[user@]host:]fs [[user@]host:]fs\n" 1105a7fbbf91SMatthew Dillon ); 1106a7fbbf91SMatthew Dillon exit(code); 1107a7fbbf91SMatthew Dillon } 1108