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 * 3401a72c9fSMatthew Dillon * $DragonFly: src/sbin/hammer/cmd_mirror.c,v 1.16 2008/11/09 05:22:56 dillon Exp $ 35a7fbbf91SMatthew Dillon */ 36a7fbbf91SMatthew Dillon 37a7fbbf91SMatthew Dillon #include "hammer.h" 38a7fbbf91SMatthew Dillon 39a7fbbf91SMatthew Dillon #define SERIALBUF_SIZE (512 * 1024) 40a7fbbf91SMatthew Dillon 41*ced4470dSMatthew Dillon typedef struct histogram { 42*ced4470dSMatthew Dillon hammer_tid_t tid; 43*ced4470dSMatthew Dillon u_int64_t bytes; 44*ced4470dSMatthew Dillon } *histogram_t; 45*ced4470dSMatthew Dillon 46a7fbbf91SMatthew Dillon static int read_mrecords(int fd, char *buf, u_int size, 4717dd83bcSMatthew Dillon hammer_ioc_mrecord_head_t pickup); 48e7f926a5SMatthew Dillon static int generate_histogram(int fd, const char *filesystem, 49*ced4470dSMatthew Dillon histogram_t *histogram_ary, 50e7f926a5SMatthew Dillon struct hammer_ioc_mirror_rw *mirror_base); 5117dd83bcSMatthew Dillon static hammer_ioc_mrecord_any_t read_mrecord(int fdin, int *errorp, 5217dd83bcSMatthew Dillon hammer_ioc_mrecord_head_t pickup); 5317dd83bcSMatthew Dillon static void write_mrecord(int fdout, u_int32_t type, 5417dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec, int bytes); 55e7f926a5SMatthew Dillon static void generate_mrec_header(int fd, int pfs_id, 56e7f926a5SMatthew Dillon union hammer_ioc_mrecord_any *mrec_tmp); 5748eadef9SMatthew Dillon static int validate_mrec_header(int fd, int fdin, int is_target, int pfs_id, 5848eadef9SMatthew Dillon struct hammer_ioc_mrecord_head *pickup, 5934ebae70SMatthew Dillon hammer_tid_t *tid_begp, hammer_tid_t *tid_endp); 60d4e5b69bSMatthew Dillon static void update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id); 6148eadef9SMatthew Dillon static ssize_t writebw(int fd, const void *buf, size_t nbytes, 6248eadef9SMatthew Dillon u_int64_t *bwcount, struct timeval *tv1); 6301a72c9fSMatthew Dillon static int getyn(void); 64a7fbbf91SMatthew Dillon static void mirror_usage(int code); 65a7fbbf91SMatthew Dillon 6617dd83bcSMatthew Dillon /* 6717dd83bcSMatthew Dillon * Generate a mirroring data stream from the specific source over the 6817dd83bcSMatthew Dillon * entire key range, but restricted to the specified transaction range. 6917dd83bcSMatthew Dillon * 7017dd83bcSMatthew Dillon * The HAMMER VFS does most of the work, we add a few new mrecord 7117dd83bcSMatthew Dillon * types to negotiate the TID ranges and verify that the entire 7217dd83bcSMatthew Dillon * stream made it to the destination. 7317dd83bcSMatthew Dillon */ 74a7fbbf91SMatthew Dillon void 7548eadef9SMatthew Dillon hammer_cmd_mirror_read(char **av, int ac, int streaming) 76a7fbbf91SMatthew Dillon { 77a7fbbf91SMatthew Dillon struct hammer_ioc_mirror_rw mirror; 78d4e5b69bSMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 7917dd83bcSMatthew Dillon union hammer_ioc_mrecord_any mrec_tmp; 8048eadef9SMatthew Dillon struct hammer_ioc_mrecord_head pickup; 8117dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 82243ca327SMatthew Dillon hammer_tid_t sync_tid; 83*ced4470dSMatthew Dillon histogram_t histogram_ary; 84a7fbbf91SMatthew Dillon const char *filesystem; 85a7fbbf91SMatthew Dillon char *buf = malloc(SERIALBUF_SIZE); 86243ca327SMatthew Dillon int interrupted = 0; 87243ca327SMatthew Dillon int error; 88a7fbbf91SMatthew Dillon int fd; 89243ca327SMatthew Dillon int n; 9048eadef9SMatthew Dillon int didwork; 91e7f926a5SMatthew Dillon int histogram; 92*ced4470dSMatthew Dillon int histindex; 93*ced4470dSMatthew Dillon int histmax; 9448eadef9SMatthew Dillon int64_t total_bytes; 95243ca327SMatthew Dillon time_t base_t = time(NULL); 9648eadef9SMatthew Dillon struct timeval bwtv; 9748eadef9SMatthew Dillon u_int64_t bwcount; 98*ced4470dSMatthew Dillon u_int64_t estbytes; 99a7fbbf91SMatthew Dillon 10034bb69d8SThomas Nikolajsen if (ac == 0 || ac > 2) 101a7fbbf91SMatthew Dillon mirror_usage(1); 102a7fbbf91SMatthew Dillon filesystem = av[0]; 103a7fbbf91SMatthew Dillon 10448eadef9SMatthew Dillon pickup.signature = 0; 10548eadef9SMatthew Dillon pickup.type = 0; 106*ced4470dSMatthew Dillon histogram = 0; 107*ced4470dSMatthew Dillon histindex = 0; 108*ced4470dSMatthew Dillon histmax = 0; 109e7f926a5SMatthew Dillon histogram_ary = NULL; 11048eadef9SMatthew Dillon 11148eadef9SMatthew Dillon again: 112a7fbbf91SMatthew Dillon bzero(&mirror, sizeof(mirror)); 113a7fbbf91SMatthew Dillon hammer_key_beg_init(&mirror.key_beg); 114a7fbbf91SMatthew Dillon hammer_key_end_init(&mirror.key_end); 115a7fbbf91SMatthew Dillon 116d4e5b69bSMatthew Dillon fd = getpfs(&pfs, filesystem); 117a7fbbf91SMatthew Dillon 1183d7b2393SMatthew Dillon if (streaming && VerboseOpt && VerboseOpt < 2) { 11948eadef9SMatthew Dillon fprintf(stderr, "\nRunning"); 12048eadef9SMatthew Dillon fflush(stderr); 12148eadef9SMatthew Dillon } 12248eadef9SMatthew Dillon total_bytes = 0; 12348eadef9SMatthew Dillon gettimeofday(&bwtv, NULL); 12448eadef9SMatthew Dillon bwcount = 0; 12548eadef9SMatthew Dillon 126243ca327SMatthew Dillon /* 127e7f926a5SMatthew Dillon * Send initial header for the purpose of determining the 128e7f926a5SMatthew Dillon * shared-uuid. 12901a72c9fSMatthew Dillon */ 130e7f926a5SMatthew Dillon generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp); 131e7f926a5SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_PFSD, 132e7f926a5SMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.pfs)); 13301a72c9fSMatthew Dillon 13401a72c9fSMatthew Dillon /* 135d4e5b69bSMatthew Dillon * In 2-way mode the target will send us a PFS info packet 136d4e5b69bSMatthew Dillon * first. Use the target's current snapshot TID as our default 137d4e5b69bSMatthew Dillon * begin TID. 138243ca327SMatthew Dillon */ 139d4e5b69bSMatthew Dillon mirror.tid_beg = 0; 14048eadef9SMatthew Dillon if (TwoWayPipeOpt) { 14148eadef9SMatthew Dillon n = validate_mrec_header(fd, 0, 0, pfs.pfs_id, &pickup, 142d4e5b69bSMatthew Dillon NULL, &mirror.tid_beg); 14348eadef9SMatthew Dillon if (n < 0) { /* got TERM record */ 14448eadef9SMatthew Dillon relpfs(fd, &pfs); 14548eadef9SMatthew Dillon return; 14648eadef9SMatthew Dillon } 14748eadef9SMatthew Dillon ++mirror.tid_beg; 14848eadef9SMatthew Dillon } 149d4e5b69bSMatthew Dillon 150d4e5b69bSMatthew Dillon /* 151d4e5b69bSMatthew Dillon * Write out the PFS header, tid_beg will be updated if our PFS 152d4e5b69bSMatthew Dillon * has a larger begin sync. tid_end is set to the latest source 153d4e5b69bSMatthew Dillon * TID whos flush cycle has completed. 154d4e5b69bSMatthew Dillon */ 155e7f926a5SMatthew Dillon generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp); 156e7f926a5SMatthew Dillon if (mirror.tid_beg < mrec_tmp.pfs.pfsd.sync_beg_tid) 157e7f926a5SMatthew Dillon mirror.tid_beg = mrec_tmp.pfs.pfsd.sync_beg_tid; 158e7f926a5SMatthew Dillon mirror.tid_end = mrec_tmp.pfs.pfsd.sync_end_tid; 159e7f926a5SMatthew Dillon mirror.ubuf = buf; 160e7f926a5SMatthew Dillon mirror.size = SERIALBUF_SIZE; 161e7f926a5SMatthew Dillon mirror.pfs_id = pfs.pfs_id; 162e7f926a5SMatthew Dillon mirror.shared_uuid = pfs.ondisk->shared_uuid; 163d4e5b69bSMatthew Dillon 164d4e5b69bSMatthew Dillon /* 165e7f926a5SMatthew Dillon * XXX If the histogram is exhausted and the TID delta is large 166e7f926a5SMatthew Dillon * the stream might have been offline for a while and is 167e7f926a5SMatthew Dillon * now picking it up again. Do another histogram. 168d4e5b69bSMatthew Dillon */ 169e7f926a5SMatthew Dillon #if 0 170*ced4470dSMatthew Dillon if (TwoWayPipeOpt && streaming && histogram && histindex == histend) { 171e7f926a5SMatthew Dillon if (mirror.tid_end - mirror.tid_beg > BULK_MINIMUM) 172*ced4470dSMatthew Dillon histogram = 0; 173e7f926a5SMatthew Dillon } 174e7f926a5SMatthew Dillon #endif 17534ebae70SMatthew Dillon 176e7f926a5SMatthew Dillon /* 177e7f926a5SMatthew Dillon * Initial bulk startup control, try to do some incremental 178e7f926a5SMatthew Dillon * mirroring in order to allow the stream to be killed and 179e7f926a5SMatthew Dillon * restarted without having to start over. 180e7f926a5SMatthew Dillon */ 181*ced4470dSMatthew Dillon if (histogram == 0 && BulkOpt == 0) { 182e7f926a5SMatthew Dillon if (VerboseOpt) 183e7f926a5SMatthew Dillon fprintf(stderr, "\n"); 184*ced4470dSMatthew Dillon histmax = generate_histogram(fd, filesystem, 185e7f926a5SMatthew Dillon &histogram_ary, &mirror); 186*ced4470dSMatthew Dillon histindex = 0; 187*ced4470dSMatthew Dillon histogram = 1; 188e7f926a5SMatthew Dillon } 189e7f926a5SMatthew Dillon 190*ced4470dSMatthew Dillon if (TwoWayPipeOpt && streaming && histogram) { 191*ced4470dSMatthew Dillon ++histindex; 192*ced4470dSMatthew Dillon mirror.tid_end = histogram_ary[histindex].tid; 193*ced4470dSMatthew Dillon estbytes = histogram_ary[histindex-1].bytes; 194e7f926a5SMatthew Dillon mrec_tmp.pfs.pfsd.sync_end_tid = mirror.tid_end; 195*ced4470dSMatthew Dillon } else { 196*ced4470dSMatthew Dillon estbytes = 0; 197e7f926a5SMatthew Dillon } 198e7f926a5SMatthew Dillon 199e7f926a5SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_PFSD, 200e7f926a5SMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.pfs)); 201e7f926a5SMatthew Dillon 202e7f926a5SMatthew Dillon /* 203e7f926a5SMatthew Dillon * A cycle file overrides the beginning TID only if we are 204e7f926a5SMatthew Dillon * not operating in two-way mode. 205e7f926a5SMatthew Dillon */ 206e7f926a5SMatthew Dillon if (TwoWayPipeOpt == 0) { 207e7f926a5SMatthew Dillon hammer_get_cycle(&mirror.key_beg, &mirror.tid_beg); 208e7f926a5SMatthew Dillon } 209e7f926a5SMatthew Dillon 210e7f926a5SMatthew Dillon /* 211e7f926a5SMatthew Dillon * An additional argument overrides the beginning TID regardless 212e7f926a5SMatthew Dillon * of what mode we are in. This is not recommending if operating 213e7f926a5SMatthew Dillon * in two-way mode. 214e7f926a5SMatthew Dillon */ 21517dd83bcSMatthew Dillon if (ac == 2) 21617dd83bcSMatthew Dillon mirror.tid_beg = strtoull(av[1], NULL, 0); 21717dd83bcSMatthew Dillon 21848eadef9SMatthew Dillon if (streaming == 0 || VerboseOpt >= 2) { 21948eadef9SMatthew Dillon fprintf(stderr, 220*ced4470dSMatthew Dillon "Mirror-read: Mirror %016jx to %016jx", 221a276dc6bSMatthew Dillon (uintmax_t)mirror.tid_beg, (uintmax_t)mirror.tid_end); 222*ced4470dSMatthew Dillon if (histogram) 223*ced4470dSMatthew Dillon fprintf(stderr, " (bulk= %ju)", (uintmax_t)estbytes); 2243d7b2393SMatthew Dillon fprintf(stderr, "\n"); 2253d7b2393SMatthew Dillon fflush(stderr); 22648eadef9SMatthew Dillon } 227243ca327SMatthew Dillon if (mirror.key_beg.obj_id != (int64_t)HAMMER_MIN_OBJID) { 228a276dc6bSMatthew Dillon fprintf(stderr, "Mirror-read: Resuming at object %016jx\n", 229a276dc6bSMatthew Dillon (uintmax_t)mirror.key_beg.obj_id); 230243ca327SMatthew Dillon } 231243ca327SMatthew Dillon 232243ca327SMatthew Dillon /* 2339dc76cb1SMatthew Dillon * Nothing to do if begin equals end. 2349dc76cb1SMatthew Dillon */ 23548eadef9SMatthew Dillon if (mirror.tid_beg >= mirror.tid_end) { 23648eadef9SMatthew Dillon if (streaming == 0 || VerboseOpt >= 2) 23748eadef9SMatthew Dillon fprintf(stderr, "Mirror-read: No work to do\n"); 23848eadef9SMatthew Dillon didwork = 0; 2399dc76cb1SMatthew Dillon goto done; 2409dc76cb1SMatthew Dillon } 24148eadef9SMatthew Dillon didwork = 1; 2429dc76cb1SMatthew Dillon 2439dc76cb1SMatthew Dillon /* 244243ca327SMatthew Dillon * Write out bulk records 245243ca327SMatthew Dillon */ 246a7fbbf91SMatthew Dillon mirror.ubuf = buf; 247a7fbbf91SMatthew Dillon mirror.size = SERIALBUF_SIZE; 248a7fbbf91SMatthew Dillon 249a7fbbf91SMatthew Dillon do { 250a7fbbf91SMatthew Dillon mirror.count = 0; 251d4e5b69bSMatthew Dillon mirror.pfs_id = pfs.pfs_id; 252d4e5b69bSMatthew Dillon mirror.shared_uuid = pfs.ondisk->shared_uuid; 253a7fbbf91SMatthew Dillon if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) { 254a7fbbf91SMatthew Dillon fprintf(stderr, "Mirror-read %s failed: %s\n", 255a7fbbf91SMatthew Dillon filesystem, strerror(errno)); 256a7fbbf91SMatthew Dillon exit(1); 257a7fbbf91SMatthew Dillon } 2589c67b4d2SMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) { 2599c67b4d2SMatthew Dillon fprintf(stderr, 2609c67b4d2SMatthew Dillon "Mirror-read %s fatal error %d\n", 2619c67b4d2SMatthew Dillon filesystem, mirror.head.error); 2629c67b4d2SMatthew Dillon exit(1); 2639c67b4d2SMatthew Dillon } 264243ca327SMatthew Dillon if (mirror.count) { 26548eadef9SMatthew Dillon if (BandwidthOpt) { 26648eadef9SMatthew Dillon n = writebw(1, mirror.ubuf, mirror.count, 26748eadef9SMatthew Dillon &bwcount, &bwtv); 26848eadef9SMatthew Dillon } else { 269243ca327SMatthew Dillon n = write(1, mirror.ubuf, mirror.count); 27048eadef9SMatthew Dillon } 271243ca327SMatthew Dillon if (n != mirror.count) { 272243ca327SMatthew Dillon fprintf(stderr, "Mirror-read %s failed: " 273243ca327SMatthew Dillon "short write\n", 274243ca327SMatthew Dillon filesystem); 275243ca327SMatthew Dillon exit(1); 276243ca327SMatthew Dillon } 277a7fbbf91SMatthew Dillon } 27848eadef9SMatthew Dillon total_bytes += mirror.count; 27948eadef9SMatthew Dillon if (streaming && VerboseOpt) { 280e7f926a5SMatthew Dillon fprintf(stderr, 281a276dc6bSMatthew Dillon "\robj=%016jx tids=%016jx:%016jx %11jd", 282a276dc6bSMatthew Dillon (uintmax_t)mirror.key_cur.obj_id, 283a276dc6bSMatthew Dillon (uintmax_t)mirror.tid_beg, 284a276dc6bSMatthew Dillon (uintmax_t)mirror.tid_end, 285a276dc6bSMatthew Dillon (intmax_t)total_bytes); 28648eadef9SMatthew Dillon fflush(stderr); 28748eadef9SMatthew Dillon } 288a7fbbf91SMatthew Dillon mirror.key_beg = mirror.key_cur; 289e7f926a5SMatthew Dillon 290e7f926a5SMatthew Dillon /* 291e7f926a5SMatthew Dillon * Deal with time limit option 292e7f926a5SMatthew Dillon */ 293243ca327SMatthew Dillon if (TimeoutOpt && 294243ca327SMatthew Dillon (unsigned)(time(NULL) - base_t) > (unsigned)TimeoutOpt) { 295243ca327SMatthew Dillon fprintf(stderr, 296243ca327SMatthew Dillon "Mirror-read %s interrupted by timer at" 297a276dc6bSMatthew Dillon " %016jx\n", 298243ca327SMatthew Dillon filesystem, 299a276dc6bSMatthew Dillon (uintmax_t)mirror.key_cur.obj_id); 300243ca327SMatthew Dillon interrupted = 1; 301243ca327SMatthew Dillon break; 302243ca327SMatthew Dillon } 303a7fbbf91SMatthew Dillon } while (mirror.count != 0); 304a7fbbf91SMatthew Dillon 30548eadef9SMatthew Dillon done: 306*ced4470dSMatthew Dillon if (streaming && VerboseOpt) { 307*ced4470dSMatthew Dillon fprintf(stderr, "\n"); 308*ced4470dSMatthew Dillon fflush(stderr); 309*ced4470dSMatthew Dillon } 310*ced4470dSMatthew Dillon 311243ca327SMatthew Dillon /* 31248eadef9SMatthew Dillon * Write out the termination sync record - only if not interrupted 313243ca327SMatthew Dillon */ 31448eadef9SMatthew Dillon if (interrupted == 0) { 31548eadef9SMatthew Dillon if (didwork) { 31617dd83bcSMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_SYNC, 31717dd83bcSMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.sync)); 31848eadef9SMatthew Dillon } else { 31948eadef9SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_IDLE, 32048eadef9SMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.sync)); 32148eadef9SMatthew Dillon } 32248eadef9SMatthew Dillon } 32334ebae70SMatthew Dillon 324243ca327SMatthew Dillon /* 325243ca327SMatthew Dillon * If the -2 option was given (automatic when doing mirror-copy), 326243ca327SMatthew Dillon * a two-way pipe is assumed and we expect a response mrec from 327243ca327SMatthew Dillon * the target. 328243ca327SMatthew Dillon */ 329243ca327SMatthew Dillon if (TwoWayPipeOpt) { 33048eadef9SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 33117dd83bcSMatthew Dillon if (mrec == NULL || 33217dd83bcSMatthew Dillon mrec->head.type != HAMMER_MREC_TYPE_UPDATE || 33317dd83bcSMatthew Dillon mrec->head.rec_size != sizeof(mrec->update)) { 334243ca327SMatthew Dillon fprintf(stderr, "mirror_read: Did not get final " 335243ca327SMatthew Dillon "acknowledgement packet from target\n"); 336243ca327SMatthew Dillon exit(1); 337243ca327SMatthew Dillon } 338243ca327SMatthew Dillon if (interrupted) { 339243ca327SMatthew Dillon if (CyclePath) { 340*ced4470dSMatthew Dillon hammer_set_cycle(&mirror.key_cur, 341*ced4470dSMatthew Dillon mirror.tid_beg); 34234bb69d8SThomas Nikolajsen fprintf(stderr, "Cyclefile %s updated for " 34334bb69d8SThomas Nikolajsen "continuation\n", CyclePath); 344243ca327SMatthew Dillon } 345243ca327SMatthew Dillon } else { 34617dd83bcSMatthew Dillon sync_tid = mrec->update.tid; 347243ca327SMatthew Dillon if (CyclePath) { 348243ca327SMatthew Dillon hammer_key_beg_init(&mirror.key_beg); 349243ca327SMatthew Dillon hammer_set_cycle(&mirror.key_beg, sync_tid); 350a276dc6bSMatthew Dillon fprintf(stderr, 351a276dc6bSMatthew Dillon "Cyclefile %s updated to 0x%016jx\n", 352a276dc6bSMatthew Dillon CyclePath, (uintmax_t)sync_tid); 353243ca327SMatthew Dillon } 354243ca327SMatthew Dillon } 355243ca327SMatthew Dillon } else if (CyclePath) { 356243ca327SMatthew Dillon /* NOTE! mirror.tid_beg cannot be updated */ 357243ca327SMatthew Dillon fprintf(stderr, "Warning: cycle file (-c option) cannot be " 358243ca327SMatthew Dillon "fully updated unless you use mirror-copy\n"); 359243ca327SMatthew Dillon hammer_set_cycle(&mirror.key_beg, mirror.tid_beg); 360243ca327SMatthew Dillon } 36148eadef9SMatthew Dillon if (streaming && interrupted == 0) { 36248eadef9SMatthew Dillon time_t t1 = time(NULL); 36348eadef9SMatthew Dillon time_t t2; 36448eadef9SMatthew Dillon 365e7f926a5SMatthew Dillon /* 366e7f926a5SMatthew Dillon * Two way streaming tries to break down large bulk 367e7f926a5SMatthew Dillon * transfers into smaller ones so it can sync the 368e7f926a5SMatthew Dillon * transaction id on the slave. This way if we get 369e7f926a5SMatthew Dillon * interrupted a restart doesn't have to start from 370e7f926a5SMatthew Dillon * scratch. 371e7f926a5SMatthew Dillon */ 372*ced4470dSMatthew Dillon if (TwoWayPipeOpt && streaming && histogram) { 373*ced4470dSMatthew Dillon if (histindex != histmax) { 3743d7b2393SMatthew Dillon if (VerboseOpt && VerboseOpt < 2) 375e7f926a5SMatthew Dillon fprintf(stderr, " (bulk incremental)"); 376e7f926a5SMatthew Dillon goto again; 377e7f926a5SMatthew Dillon } 378*ced4470dSMatthew Dillon } 379e7f926a5SMatthew Dillon 38048eadef9SMatthew Dillon if (VerboseOpt) { 38148eadef9SMatthew Dillon fprintf(stderr, " W"); 38248eadef9SMatthew Dillon fflush(stderr); 38348eadef9SMatthew Dillon } 38448eadef9SMatthew Dillon pfs.ondisk->sync_end_tid = mirror.tid_end; 38548eadef9SMatthew Dillon if (ioctl(fd, HAMMERIOC_WAI_PSEUDOFS, &pfs) < 0) { 38648eadef9SMatthew Dillon fprintf(stderr, "Mirror-read %s: cannot stream: %s\n", 38748eadef9SMatthew Dillon filesystem, strerror(errno)); 38848eadef9SMatthew Dillon } else { 38948eadef9SMatthew Dillon t2 = time(NULL) - t1; 39048eadef9SMatthew Dillon if (t2 >= 0 && t2 < DelayOpt) { 39148eadef9SMatthew Dillon if (VerboseOpt) { 39248eadef9SMatthew Dillon fprintf(stderr, "\bD"); 39348eadef9SMatthew Dillon fflush(stderr); 39448eadef9SMatthew Dillon } 39548eadef9SMatthew Dillon sleep(DelayOpt - t2); 39648eadef9SMatthew Dillon } 39748eadef9SMatthew Dillon if (VerboseOpt) { 39848eadef9SMatthew Dillon fprintf(stderr, "\b "); 39948eadef9SMatthew Dillon fflush(stderr); 40048eadef9SMatthew Dillon } 40148eadef9SMatthew Dillon relpfs(fd, &pfs); 40248eadef9SMatthew Dillon goto again; 40348eadef9SMatthew Dillon } 40448eadef9SMatthew Dillon } 40548eadef9SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_TERM, 40648eadef9SMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.sync)); 40748eadef9SMatthew Dillon relpfs(fd, &pfs); 408a7fbbf91SMatthew Dillon fprintf(stderr, "Mirror-read %s succeeded\n", filesystem); 409a7fbbf91SMatthew Dillon } 410a7fbbf91SMatthew Dillon 411e7f926a5SMatthew Dillon /* 4123d7b2393SMatthew Dillon * What we are trying to do here is figure out how much data is 4133d7b2393SMatthew Dillon * going to be sent for the TID range and to break the TID range 4143d7b2393SMatthew Dillon * down into reasonably-sized slices (from the point of view of 4153d7b2393SMatthew Dillon * data sent) so a lost connection can restart at a reasonable 4163d7b2393SMatthew Dillon * place and not all the way back at the beginning. 417*ced4470dSMatthew Dillon * 418*ced4470dSMatthew Dillon * An entry's TID serves as the end_tid for the prior entry 419*ced4470dSMatthew Dillon * So we have to offset the calculation by 1 so that TID falls into 420*ced4470dSMatthew Dillon * the previous entry when populating entries. 421*ced4470dSMatthew Dillon * 422*ced4470dSMatthew Dillon * Because the transaction id space is bursty we need a relatively 423*ced4470dSMatthew Dillon * large number of buckets (like a million) to do a reasonable job 424*ced4470dSMatthew Dillon * for things like an initial bulk mirrors on a very large filesystem. 425e7f926a5SMatthew Dillon */ 426*ced4470dSMatthew Dillon #define HIST_COUNT (1024 * 1024) 4273d7b2393SMatthew Dillon 428e7f926a5SMatthew Dillon static int 429e7f926a5SMatthew Dillon generate_histogram(int fd, const char *filesystem, 430*ced4470dSMatthew Dillon histogram_t *histogram_ary, 431e7f926a5SMatthew Dillon struct hammer_ioc_mirror_rw *mirror_base) 432e7f926a5SMatthew Dillon { 433e7f926a5SMatthew Dillon struct hammer_ioc_mirror_rw mirror; 4343d7b2393SMatthew Dillon union hammer_ioc_mrecord_any *mrec; 435e7f926a5SMatthew Dillon hammer_tid_t tid_beg; 436e7f926a5SMatthew Dillon hammer_tid_t tid_end; 437*ced4470dSMatthew Dillon hammer_tid_t tid; 438*ced4470dSMatthew Dillon hammer_tid_t tidx; 439*ced4470dSMatthew Dillon u_int64_t *tid_bytes; 4403d7b2393SMatthew Dillon u_int64_t total; 4413d7b2393SMatthew Dillon u_int64_t accum; 442e7f926a5SMatthew Dillon int i; 4433d7b2393SMatthew Dillon int res; 4443d7b2393SMatthew Dillon int off; 4453d7b2393SMatthew Dillon int len; 446e7f926a5SMatthew Dillon 447e7f926a5SMatthew Dillon mirror = *mirror_base; 448e7f926a5SMatthew Dillon tid_beg = mirror.tid_beg; 449e7f926a5SMatthew Dillon tid_end = mirror.tid_end; 4503d7b2393SMatthew Dillon mirror.head.flags |= HAMMER_IOC_MIRROR_NODATA; 451e7f926a5SMatthew Dillon 4523d7b2393SMatthew Dillon if (*histogram_ary == NULL) { 453*ced4470dSMatthew Dillon *histogram_ary = malloc(sizeof(struct histogram) * 4543d7b2393SMatthew Dillon (HIST_COUNT + 2)); 4553d7b2393SMatthew Dillon } 4563d7b2393SMatthew Dillon if (tid_beg >= tid_end) 457e7f926a5SMatthew Dillon return(0); 458e7f926a5SMatthew Dillon 459*ced4470dSMatthew Dillon /* needs 2 extra */ 460*ced4470dSMatthew Dillon tid_bytes = malloc(sizeof(*tid_bytes) * (HIST_COUNT + 2)); 461*ced4470dSMatthew Dillon bzero(tid_bytes, sizeof(tid_bytes)); 462*ced4470dSMatthew Dillon 4633d7b2393SMatthew Dillon fprintf(stderr, "Prescan to break up bulk transfer"); 4643d7b2393SMatthew Dillon if (VerboseOpt > 1) 4653d7b2393SMatthew Dillon fprintf(stderr, " (%juMB chunks)", 4663d7b2393SMatthew Dillon (uintmax_t)(SplitupOpt / (1024 * 1024))); 4673d7b2393SMatthew Dillon fprintf(stderr, "\n"); 468e7f926a5SMatthew Dillon 469*ced4470dSMatthew Dillon /* 470*ced4470dSMatthew Dillon * Note: (tid_beg,tid_end), range is inclusive of both beg & end. 471*ced4470dSMatthew Dillon * 472*ced4470dSMatthew Dillon * Note: Estimates can be off when the mirror is way behind due 473*ced4470dSMatthew Dillon * to skips. 474*ced4470dSMatthew Dillon */ 4753d7b2393SMatthew Dillon total = 0; 4763d7b2393SMatthew Dillon accum = 0; 4773d7b2393SMatthew Dillon for (;;) { 4783d7b2393SMatthew Dillon mirror.count = 0; 479e7f926a5SMatthew Dillon if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) { 480e7f926a5SMatthew Dillon fprintf(stderr, "Mirror-read %s failed: %s\n", 481e7f926a5SMatthew Dillon filesystem, strerror(errno)); 482e7f926a5SMatthew Dillon exit(1); 483e7f926a5SMatthew Dillon } 484e7f926a5SMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) { 485e7f926a5SMatthew Dillon fprintf(stderr, 486e7f926a5SMatthew Dillon "Mirror-read %s fatal error %d\n", 487e7f926a5SMatthew Dillon filesystem, mirror.head.error); 488e7f926a5SMatthew Dillon exit(1); 489e7f926a5SMatthew Dillon } 4903d7b2393SMatthew Dillon for (off = 0; 4913d7b2393SMatthew Dillon off < mirror.count; 4923d7b2393SMatthew Dillon off += HAMMER_HEAD_DOALIGN(mrec->head.rec_size) 4933d7b2393SMatthew Dillon ) { 4943d7b2393SMatthew Dillon mrec = (void *)((char *)mirror.ubuf + off); 495e7f926a5SMatthew Dillon 496e7f926a5SMatthew Dillon /* 497*ced4470dSMatthew Dillon * We only care about general RECs and PASS 498*ced4470dSMatthew Dillon * records. We ignore SKIPs. 499e7f926a5SMatthew Dillon */ 500*ced4470dSMatthew Dillon switch (mrec->head.type & HAMMER_MRECF_TYPE_LOMASK) { 501*ced4470dSMatthew Dillon case HAMMER_MREC_TYPE_REC: 502*ced4470dSMatthew Dillon case HAMMER_MREC_TYPE_PASS: 503*ced4470dSMatthew Dillon break; 504*ced4470dSMatthew Dillon default: 505*ced4470dSMatthew Dillon continue; 506e7f926a5SMatthew Dillon } 5073d7b2393SMatthew Dillon 508*ced4470dSMatthew Dillon /* 509*ced4470dSMatthew Dillon * Calculate for two indices, create_tid and 510*ced4470dSMatthew Dillon * delete_tid. Record data only applies to 511*ced4470dSMatthew Dillon * the create_tid. 512*ced4470dSMatthew Dillon * 513*ced4470dSMatthew Dillon * When tid is exactly on the boundary it really 514*ced4470dSMatthew Dillon * belongs to the previous entry because scans 515*ced4470dSMatthew Dillon * are inclusive of the ending entry. 516*ced4470dSMatthew Dillon */ 517*ced4470dSMatthew Dillon tid = mrec->rec.leaf.base.delete_tid; 518*ced4470dSMatthew Dillon if (tid && tid >= tid_beg && tid <= tid_end) { 519*ced4470dSMatthew Dillon len = HAMMER_HEAD_DOALIGN(mrec->head.rec_size); 520*ced4470dSMatthew Dillon if (mrec->head.type == 521*ced4470dSMatthew Dillon HAMMER_MREC_TYPE_REC) { 522*ced4470dSMatthew Dillon len -= HAMMER_HEAD_DOALIGN( 523*ced4470dSMatthew Dillon mrec->rec.leaf.data_len); 524*ced4470dSMatthew Dillon assert(len > 0); 525*ced4470dSMatthew Dillon } 526*ced4470dSMatthew Dillon i = (tid - tid_beg) * HIST_COUNT / 5273d7b2393SMatthew Dillon (tid_end - tid_beg); 528*ced4470dSMatthew Dillon tidx = tid_beg + i * (tid_end - tid_beg) / 529*ced4470dSMatthew Dillon HIST_COUNT; 530*ced4470dSMatthew Dillon if (tid == tidx && i) 531*ced4470dSMatthew Dillon --i; 532*ced4470dSMatthew Dillon assert(i >= 0 && i < HIST_COUNT); 5333d7b2393SMatthew Dillon tid_bytes[i] += len; 5343d7b2393SMatthew Dillon total += len; 5353d7b2393SMatthew Dillon accum += len; 5363d7b2393SMatthew Dillon } 537*ced4470dSMatthew Dillon 538*ced4470dSMatthew Dillon tid = mrec->rec.leaf.base.create_tid; 539*ced4470dSMatthew Dillon if (tid && tid >= tid_beg && tid <= tid_end) { 540*ced4470dSMatthew Dillon len = HAMMER_HEAD_DOALIGN(mrec->head.rec_size); 541*ced4470dSMatthew Dillon if (mrec->head.type == 542*ced4470dSMatthew Dillon HAMMER_MREC_TYPE_REC_NODATA) { 543*ced4470dSMatthew Dillon len += HAMMER_HEAD_DOALIGN( 544*ced4470dSMatthew Dillon mrec->rec.leaf.data_len); 545*ced4470dSMatthew Dillon } 546*ced4470dSMatthew Dillon i = (tid - tid_beg) * HIST_COUNT / 547*ced4470dSMatthew Dillon (tid_end - tid_beg); 548*ced4470dSMatthew Dillon tidx = tid_beg + i * (tid_end - tid_beg) / 549*ced4470dSMatthew Dillon HIST_COUNT; 550*ced4470dSMatthew Dillon if (tid == tidx && i) 551*ced4470dSMatthew Dillon --i; 552*ced4470dSMatthew Dillon assert(i >= 0 && i < HIST_COUNT); 553*ced4470dSMatthew Dillon tid_bytes[i] += len; 554*ced4470dSMatthew Dillon total += len; 555*ced4470dSMatthew Dillon accum += len; 5563d7b2393SMatthew Dillon } 5573d7b2393SMatthew Dillon } 5583d7b2393SMatthew Dillon if (VerboseOpt > 1) { 5593d7b2393SMatthew Dillon if (accum > SplitupOpt) { 5603d7b2393SMatthew Dillon fprintf(stderr, "."); 5613d7b2393SMatthew Dillon fflush(stderr); 5623d7b2393SMatthew Dillon accum = 0; 5633d7b2393SMatthew Dillon } 5643d7b2393SMatthew Dillon } 5653d7b2393SMatthew Dillon if (mirror.count == 0) 5663d7b2393SMatthew Dillon break; 5673d7b2393SMatthew Dillon mirror.key_beg = mirror.key_cur; 5683d7b2393SMatthew Dillon } 5693d7b2393SMatthew Dillon 5703d7b2393SMatthew Dillon /* 5713d7b2393SMatthew Dillon * Reduce to SplitupOpt (default 100MB) chunks. This code may 572*ced4470dSMatthew Dillon * use up to two additional elements. Do the array in-place. 573*ced4470dSMatthew Dillon * 574*ced4470dSMatthew Dillon * Inefficient degenerate cases can occur if we do not accumulate 575*ced4470dSMatthew Dillon * at least the requested split amount, so error on the side of 576*ced4470dSMatthew Dillon * going over a bit. 5773d7b2393SMatthew Dillon */ 5783d7b2393SMatthew Dillon res = 0; 579*ced4470dSMatthew Dillon (*histogram_ary)[res].tid = tid_beg; 580*ced4470dSMatthew Dillon (*histogram_ary)[res].bytes = tid_bytes[0]; 581*ced4470dSMatthew Dillon for (i = 1; i < HIST_COUNT; ++i) { 582*ced4470dSMatthew Dillon if ((*histogram_ary)[res].bytes >= SplitupOpt) { 583*ced4470dSMatthew Dillon ++res; 584*ced4470dSMatthew Dillon (*histogram_ary)[res].tid = tid_beg + 5853d7b2393SMatthew Dillon i * (tid_end - tid_beg) / 5863d7b2393SMatthew Dillon HIST_COUNT; 587*ced4470dSMatthew Dillon (*histogram_ary)[res].bytes = 0; 588*ced4470dSMatthew Dillon 5893d7b2393SMatthew Dillon } 590*ced4470dSMatthew Dillon (*histogram_ary)[res].bytes += tid_bytes[i]; 5913d7b2393SMatthew Dillon } 592*ced4470dSMatthew Dillon ++res; 593*ced4470dSMatthew Dillon (*histogram_ary)[res].tid = tid_end; 594*ced4470dSMatthew Dillon (*histogram_ary)[res].bytes = -1; 595*ced4470dSMatthew Dillon 5963d7b2393SMatthew Dillon if (VerboseOpt > 1) 5973d7b2393SMatthew Dillon fprintf(stderr, "\n"); /* newline after ... */ 598*ced4470dSMatthew Dillon assert(res <= HIST_COUNT); 599*ced4470dSMatthew Dillon fprintf(stderr, "Prescan %d chunks, total %ju MBytes (\n", 6003d7b2393SMatthew Dillon res, (uintmax_t)total / (1024 * 1024)); 601*ced4470dSMatthew Dillon for (i = 0; i < res && i < 3; ++i) { 602*ced4470dSMatthew Dillon if (i) 603*ced4470dSMatthew Dillon fprintf(stderr, ", "); 604*ced4470dSMatthew Dillon fprintf(stderr, "%ju", (uintmax_t)(*histogram_ary)[i].bytes); 605*ced4470dSMatthew Dillon } 606*ced4470dSMatthew Dillon if (i < res) 607*ced4470dSMatthew Dillon fprintf(stderr, ", ..."); 608*ced4470dSMatthew Dillon fprintf(stderr, ")\n"); 609*ced4470dSMatthew Dillon 610*ced4470dSMatthew Dillon free(tid_bytes); 6113d7b2393SMatthew Dillon return(res); 612e7f926a5SMatthew Dillon } 613e7f926a5SMatthew Dillon 61401a72c9fSMatthew Dillon static void 61501a72c9fSMatthew Dillon create_pfs(const char *filesystem, uuid_t *s_uuid) 61601a72c9fSMatthew Dillon { 617f414d101SSascha Wildner if (ForceYesOpt == 1) { 61807485271SMichael Neumann fprintf(stderr, "PFS slave %s does not exist. " 61907485271SMichael Neumann "Auto create new slave PFS!\n", filesystem); 62007485271SMichael Neumann 621f414d101SSascha Wildner } else { 62201a72c9fSMatthew Dillon fprintf(stderr, "PFS slave %s does not exist.\n" 62301a72c9fSMatthew Dillon "Do you want to create a new slave PFS? (yes|no) ", 62401a72c9fSMatthew Dillon filesystem); 62501a72c9fSMatthew Dillon fflush(stderr); 62601a72c9fSMatthew Dillon if (getyn() != 1) { 62701a72c9fSMatthew Dillon fprintf(stderr, "Aborting operation\n"); 62801a72c9fSMatthew Dillon exit(1); 62901a72c9fSMatthew Dillon } 63007485271SMichael Neumann } 63101a72c9fSMatthew Dillon 63201a72c9fSMatthew Dillon u_int32_t status; 63301a72c9fSMatthew Dillon char *shared_uuid = NULL; 63401a72c9fSMatthew Dillon uuid_to_string(s_uuid, &shared_uuid, &status); 63501a72c9fSMatthew Dillon 63601a72c9fSMatthew Dillon char *cmd = NULL; 63701a72c9fSMatthew Dillon asprintf(&cmd, "/sbin/hammer pfs-slave '%s' shared-uuid=%s 1>&2", 63801a72c9fSMatthew Dillon filesystem, shared_uuid); 63901a72c9fSMatthew Dillon free(shared_uuid); 64001a72c9fSMatthew Dillon 64101a72c9fSMatthew Dillon if (cmd == NULL) { 64201a72c9fSMatthew Dillon fprintf(stderr, "Failed to alloc memory\n"); 64301a72c9fSMatthew Dillon exit(1); 64401a72c9fSMatthew Dillon } 64501a72c9fSMatthew Dillon if (system(cmd) != 0) { 64601a72c9fSMatthew Dillon fprintf(stderr, "Failed to create PFS\n"); 64701a72c9fSMatthew Dillon } 64801a72c9fSMatthew Dillon free(cmd); 64901a72c9fSMatthew Dillon } 65001a72c9fSMatthew Dillon 65117dd83bcSMatthew Dillon /* 65217dd83bcSMatthew Dillon * Pipe the mirroring data stream on stdin to the HAMMER VFS, adding 65317dd83bcSMatthew Dillon * some additional packet types to negotiate TID ranges and to verify 65417dd83bcSMatthew Dillon * completion. The HAMMER VFS does most of the work. 65517dd83bcSMatthew Dillon * 65617dd83bcSMatthew Dillon * It is important to note that the mirror.key_{beg,end} range must 65717dd83bcSMatthew Dillon * match the ranged used by the original. For now both sides use 65817dd83bcSMatthew Dillon * range the entire key space. 65917dd83bcSMatthew Dillon * 66017dd83bcSMatthew Dillon * It is even more important that the records in the stream conform 66117dd83bcSMatthew Dillon * to the TID range also supplied in the stream. The HAMMER VFS will 66217dd83bcSMatthew Dillon * use the REC, PASS, and SKIP record types to track the portions of 66317dd83bcSMatthew Dillon * the B-Tree being scanned in order to be able to proactively delete 66417dd83bcSMatthew Dillon * records on the target within those active areas that are not mentioned 66517dd83bcSMatthew Dillon * by the source. 66617dd83bcSMatthew Dillon * 66717dd83bcSMatthew Dillon * The mirror.key_cur field is used by the VFS to do this tracking. It 66817dd83bcSMatthew Dillon * must be initialized to key_beg but then is persistently updated by 66917dd83bcSMatthew Dillon * the HAMMER VFS on each successive ioctl() call. If you blow up this 67017dd83bcSMatthew Dillon * field you will blow up the mirror target, possibly to the point of 67117dd83bcSMatthew Dillon * deleting everything. As a safety measure the HAMMER VFS simply marks 67217dd83bcSMatthew Dillon * the records that the source has destroyed as deleted on the target, 67317dd83bcSMatthew Dillon * and normal pruning operations will deal with their final disposition 67417dd83bcSMatthew Dillon * at some later time. 67517dd83bcSMatthew Dillon */ 676a7fbbf91SMatthew Dillon void 677a7fbbf91SMatthew Dillon hammer_cmd_mirror_write(char **av, int ac) 678a7fbbf91SMatthew Dillon { 679a7fbbf91SMatthew Dillon struct hammer_ioc_mirror_rw mirror; 680a7fbbf91SMatthew Dillon const char *filesystem; 681a7fbbf91SMatthew Dillon char *buf = malloc(SERIALBUF_SIZE); 682d4e5b69bSMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 68317dd83bcSMatthew Dillon struct hammer_ioc_mrecord_head pickup; 684243ca327SMatthew Dillon struct hammer_ioc_synctid synctid; 68517dd83bcSMatthew Dillon union hammer_ioc_mrecord_any mrec_tmp; 68617dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 68701a72c9fSMatthew Dillon struct stat st; 688243ca327SMatthew Dillon int error; 689243ca327SMatthew Dillon int fd; 69048eadef9SMatthew Dillon int n; 691a7fbbf91SMatthew Dillon 69234bb69d8SThomas Nikolajsen if (ac != 1) 693a7fbbf91SMatthew Dillon mirror_usage(1); 694a7fbbf91SMatthew Dillon filesystem = av[0]; 695a7fbbf91SMatthew Dillon 69648eadef9SMatthew Dillon pickup.signature = 0; 69748eadef9SMatthew Dillon pickup.type = 0; 69848eadef9SMatthew Dillon 69948eadef9SMatthew Dillon again: 700a7fbbf91SMatthew Dillon bzero(&mirror, sizeof(mirror)); 701a7fbbf91SMatthew Dillon hammer_key_beg_init(&mirror.key_beg); 702a7fbbf91SMatthew Dillon hammer_key_end_init(&mirror.key_end); 70317dd83bcSMatthew Dillon mirror.key_end = mirror.key_beg; 704a7fbbf91SMatthew Dillon 70501a72c9fSMatthew Dillon /* 70601a72c9fSMatthew Dillon * Read initial packet 70701a72c9fSMatthew Dillon */ 70801a72c9fSMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 70901a72c9fSMatthew Dillon if (mrec == NULL) { 71001a72c9fSMatthew Dillon if (error == 0) 71101a72c9fSMatthew Dillon fprintf(stderr, "validate_mrec_header: short read\n"); 71201a72c9fSMatthew Dillon exit(1); 71301a72c9fSMatthew Dillon } 71401a72c9fSMatthew Dillon /* 71501a72c9fSMatthew Dillon * Validate packet 71601a72c9fSMatthew Dillon */ 71701a72c9fSMatthew Dillon if (mrec->head.type == HAMMER_MREC_TYPE_TERM) { 71801a72c9fSMatthew Dillon return; 71901a72c9fSMatthew Dillon } 72001a72c9fSMatthew Dillon if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) { 72101a72c9fSMatthew Dillon fprintf(stderr, "validate_mrec_header: did not get expected " 72201a72c9fSMatthew Dillon "PFSD record type\n"); 72301a72c9fSMatthew Dillon exit(1); 72401a72c9fSMatthew Dillon } 72501a72c9fSMatthew Dillon if (mrec->head.rec_size != sizeof(mrec->pfs)) { 72601a72c9fSMatthew Dillon fprintf(stderr, "validate_mrec_header: unexpected payload " 72701a72c9fSMatthew Dillon "size\n"); 72801a72c9fSMatthew Dillon exit(1); 72901a72c9fSMatthew Dillon } 73001a72c9fSMatthew Dillon 73101a72c9fSMatthew Dillon /* 73201a72c9fSMatthew Dillon * Create slave PFS if it doesn't yet exist 73301a72c9fSMatthew Dillon */ 73401a72c9fSMatthew Dillon if (lstat(filesystem, &st) != 0) { 73501a72c9fSMatthew Dillon create_pfs(filesystem, &mrec->pfs.pfsd.shared_uuid); 73601a72c9fSMatthew Dillon } 73701a72c9fSMatthew Dillon free(mrec); 73801a72c9fSMatthew Dillon mrec = NULL; 73901a72c9fSMatthew Dillon 740d4e5b69bSMatthew Dillon fd = getpfs(&pfs, filesystem); 741a7fbbf91SMatthew Dillon 742243ca327SMatthew Dillon /* 743d4e5b69bSMatthew Dillon * In two-way mode the target writes out a PFS packet first. 744d4e5b69bSMatthew Dillon * The source uses our tid_end as its tid_beg by default, 745d4e5b69bSMatthew Dillon * picking up where it left off. 746243ca327SMatthew Dillon */ 747d4e5b69bSMatthew Dillon mirror.tid_beg = 0; 748d4e5b69bSMatthew Dillon if (TwoWayPipeOpt) { 749e7f926a5SMatthew Dillon generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp); 750e7f926a5SMatthew Dillon if (mirror.tid_beg < mrec_tmp.pfs.pfsd.sync_beg_tid) 751e7f926a5SMatthew Dillon mirror.tid_beg = mrec_tmp.pfs.pfsd.sync_beg_tid; 752e7f926a5SMatthew Dillon mirror.tid_end = mrec_tmp.pfs.pfsd.sync_end_tid; 753e7f926a5SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_PFSD, 754e7f926a5SMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.pfs)); 755d4e5b69bSMatthew Dillon } 756d4e5b69bSMatthew Dillon 757d4e5b69bSMatthew Dillon /* 75817dd83bcSMatthew Dillon * Read and process the PFS header. The source informs us of 75917dd83bcSMatthew Dillon * the TID range the stream represents. 760d4e5b69bSMatthew Dillon */ 76148eadef9SMatthew Dillon n = validate_mrec_header(fd, 0, 1, pfs.pfs_id, &pickup, 762d4e5b69bSMatthew Dillon &mirror.tid_beg, &mirror.tid_end); 76348eadef9SMatthew Dillon if (n < 0) { /* got TERM record */ 76448eadef9SMatthew Dillon relpfs(fd, &pfs); 76548eadef9SMatthew Dillon return; 76648eadef9SMatthew Dillon } 76734ebae70SMatthew Dillon 768a7fbbf91SMatthew Dillon mirror.ubuf = buf; 769a7fbbf91SMatthew Dillon mirror.size = SERIALBUF_SIZE; 770a7fbbf91SMatthew Dillon 771243ca327SMatthew Dillon /* 77217dd83bcSMatthew Dillon * Read and process bulk records (REC, PASS, and SKIP types). 77317dd83bcSMatthew Dillon * 77417dd83bcSMatthew Dillon * On your life, do NOT mess with mirror.key_cur or your mirror 77517dd83bcSMatthew Dillon * target may become history. 776243ca327SMatthew Dillon */ 777a7fbbf91SMatthew Dillon for (;;) { 778a7fbbf91SMatthew Dillon mirror.count = 0; 779d4e5b69bSMatthew Dillon mirror.pfs_id = pfs.pfs_id; 780d4e5b69bSMatthew Dillon mirror.shared_uuid = pfs.ondisk->shared_uuid; 781a7fbbf91SMatthew Dillon mirror.size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup); 782a7fbbf91SMatthew Dillon if (mirror.size <= 0) 783a7fbbf91SMatthew Dillon break; 784a7fbbf91SMatthew Dillon if (ioctl(fd, HAMMERIOC_MIRROR_WRITE, &mirror) < 0) { 785a7fbbf91SMatthew Dillon fprintf(stderr, "Mirror-write %s failed: %s\n", 786a7fbbf91SMatthew Dillon filesystem, strerror(errno)); 787a7fbbf91SMatthew Dillon exit(1); 788a7fbbf91SMatthew Dillon } 78917dd83bcSMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) { 79017dd83bcSMatthew Dillon fprintf(stderr, 79117dd83bcSMatthew Dillon "Mirror-write %s fatal error %d\n", 79217dd83bcSMatthew Dillon filesystem, mirror.head.error); 79317dd83bcSMatthew Dillon exit(1); 79417dd83bcSMatthew Dillon } 795243ca327SMatthew Dillon #if 0 796a7fbbf91SMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_INTR) { 797a7fbbf91SMatthew Dillon fprintf(stderr, 798a7fbbf91SMatthew Dillon "Mirror-write %s interrupted by timer at" 799243ca327SMatthew Dillon " %016llx\n", 800a7fbbf91SMatthew Dillon filesystem, 801243ca327SMatthew Dillon mirror.key_cur.obj_id); 802a7fbbf91SMatthew Dillon exit(0); 803a7fbbf91SMatthew Dillon } 804243ca327SMatthew Dillon #endif 805a7fbbf91SMatthew Dillon } 806243ca327SMatthew Dillon 807243ca327SMatthew Dillon /* 808243ca327SMatthew Dillon * Read and process the termination sync record. 809243ca327SMatthew Dillon */ 810243ca327SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 8119dc76cb1SMatthew Dillon 8129dc76cb1SMatthew Dillon if (mrec && mrec->head.type == HAMMER_MREC_TYPE_TERM) { 81348eadef9SMatthew Dillon fprintf(stderr, "Mirror-write: received termination request\n"); 81448eadef9SMatthew Dillon free(mrec); 8159dc76cb1SMatthew Dillon return; 8169dc76cb1SMatthew Dillon } 8179dc76cb1SMatthew Dillon 81817dd83bcSMatthew Dillon if (mrec == NULL || 81948eadef9SMatthew Dillon (mrec->head.type != HAMMER_MREC_TYPE_SYNC && 82048eadef9SMatthew Dillon mrec->head.type != HAMMER_MREC_TYPE_IDLE) || 82117dd83bcSMatthew Dillon mrec->head.rec_size != sizeof(mrec->sync)) { 822243ca327SMatthew Dillon fprintf(stderr, "Mirror-write %s: Did not get termination " 82317dd83bcSMatthew Dillon "sync record, or rec_size is wrong rt=%d\n", 82417dd83bcSMatthew Dillon filesystem, mrec->head.type); 825da44aa75SMatthew Dillon exit(1); 826243ca327SMatthew Dillon } 827243ca327SMatthew Dillon 828243ca327SMatthew Dillon /* 829243ca327SMatthew Dillon * Update the PFS info on the target so the user has visibility 83048eadef9SMatthew Dillon * into the new snapshot, and sync the target filesystem. 831243ca327SMatthew Dillon */ 83248eadef9SMatthew Dillon if (mrec->head.type == HAMMER_MREC_TYPE_SYNC) { 833d4e5b69bSMatthew Dillon update_pfs_snapshot(fd, mirror.tid_end, pfs.pfs_id); 834243ca327SMatthew Dillon 835243ca327SMatthew Dillon bzero(&synctid, sizeof(synctid)); 836243ca327SMatthew Dillon synctid.op = HAMMER_SYNCTID_SYNC2; 837243ca327SMatthew Dillon ioctl(fd, HAMMERIOC_SYNCTID, &synctid); 838243ca327SMatthew Dillon 83948eadef9SMatthew Dillon if (VerboseOpt >= 2) { 84048eadef9SMatthew Dillon fprintf(stderr, "Mirror-write %s: succeeded\n", 84148eadef9SMatthew Dillon filesystem); 84248eadef9SMatthew Dillon } 84348eadef9SMatthew Dillon } 84448eadef9SMatthew Dillon 84548eadef9SMatthew Dillon free(mrec); 84648eadef9SMatthew Dillon mrec = NULL; 847243ca327SMatthew Dillon 848243ca327SMatthew Dillon /* 849243ca327SMatthew Dillon * Report back to the originator. 850243ca327SMatthew Dillon */ 851243ca327SMatthew Dillon if (TwoWayPipeOpt) { 85217dd83bcSMatthew Dillon mrec_tmp.update.tid = mirror.tid_end; 853243ca327SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_UPDATE, 85417dd83bcSMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.update)); 855243ca327SMatthew Dillon } else { 856a276dc6bSMatthew Dillon printf("Source can update synctid to 0x%016jx\n", 857a276dc6bSMatthew Dillon (uintmax_t)mirror.tid_end); 858243ca327SMatthew Dillon } 85948eadef9SMatthew Dillon relpfs(fd, &pfs); 86048eadef9SMatthew Dillon goto again; 861243ca327SMatthew Dillon } 862243ca327SMatthew Dillon 863243ca327SMatthew Dillon void 864243ca327SMatthew Dillon hammer_cmd_mirror_dump(void) 865243ca327SMatthew Dillon { 866243ca327SMatthew Dillon char *buf = malloc(SERIALBUF_SIZE); 86717dd83bcSMatthew Dillon struct hammer_ioc_mrecord_head pickup; 86817dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 869243ca327SMatthew Dillon int error; 870243ca327SMatthew Dillon int size; 87117dd83bcSMatthew Dillon int offset; 87217dd83bcSMatthew Dillon int bytes; 873243ca327SMatthew Dillon 874243ca327SMatthew Dillon /* 875243ca327SMatthew Dillon * Read and process the PFS header 876243ca327SMatthew Dillon */ 877243ca327SMatthew Dillon pickup.signature = 0; 878243ca327SMatthew Dillon pickup.type = 0; 879243ca327SMatthew Dillon 880243ca327SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 881243ca327SMatthew Dillon 882243ca327SMatthew Dillon /* 883243ca327SMatthew Dillon * Read and process bulk records 884243ca327SMatthew Dillon */ 885243ca327SMatthew Dillon for (;;) { 886243ca327SMatthew Dillon size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup); 887243ca327SMatthew Dillon if (size <= 0) 888243ca327SMatthew Dillon break; 88917dd83bcSMatthew Dillon offset = 0; 89017dd83bcSMatthew Dillon while (offset < size) { 89117dd83bcSMatthew Dillon mrec = (void *)((char *)buf + offset); 89217dd83bcSMatthew Dillon bytes = HAMMER_HEAD_DOALIGN(mrec->head.rec_size); 89317dd83bcSMatthew Dillon if (offset + bytes > size) { 89417dd83bcSMatthew Dillon fprintf(stderr, "Misaligned record\n"); 89517dd83bcSMatthew Dillon exit(1); 89617dd83bcSMatthew Dillon } 89717dd83bcSMatthew Dillon 89885a8e8a7SMatthew Dillon switch(mrec->head.type & HAMMER_MRECF_TYPE_MASK) { 89985a8e8a7SMatthew Dillon case HAMMER_MREC_TYPE_REC_BADCRC: 90017dd83bcSMatthew Dillon case HAMMER_MREC_TYPE_REC: 901a276dc6bSMatthew Dillon printf("Record obj=%016jx key=%016jx " 90285a8e8a7SMatthew Dillon "rt=%02x ot=%02x", 903a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.obj_id, 904a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.key, 90517dd83bcSMatthew Dillon mrec->rec.leaf.base.rec_type, 90617dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_type); 90785a8e8a7SMatthew Dillon if (mrec->head.type == 90885a8e8a7SMatthew Dillon HAMMER_MREC_TYPE_REC_BADCRC) { 90985a8e8a7SMatthew Dillon printf(" (BAD CRC)"); 91085a8e8a7SMatthew Dillon } 91185a8e8a7SMatthew Dillon printf("\n"); 912a276dc6bSMatthew Dillon printf(" tids %016jx:%016jx data=%d\n", 913a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.create_tid, 914a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.delete_tid, 91517dd83bcSMatthew Dillon mrec->rec.leaf.data_len); 91617dd83bcSMatthew Dillon break; 91717dd83bcSMatthew Dillon case HAMMER_MREC_TYPE_PASS: 918a276dc6bSMatthew Dillon printf("Pass obj=%016jx key=%016jx " 91917dd83bcSMatthew Dillon "rt=%02x ot=%02x\n", 920a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.obj_id, 921a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.key, 92217dd83bcSMatthew Dillon mrec->rec.leaf.base.rec_type, 92317dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_type); 924a276dc6bSMatthew Dillon printf(" tids %016jx:%016jx data=%d\n", 925a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.create_tid, 926a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.delete_tid, 92717dd83bcSMatthew Dillon mrec->rec.leaf.data_len); 92817dd83bcSMatthew Dillon break; 92917dd83bcSMatthew Dillon case HAMMER_MREC_TYPE_SKIP: 930a276dc6bSMatthew Dillon printf("Skip obj=%016jx key=%016jx rt=%02x to\n" 931a276dc6bSMatthew Dillon " obj=%016jx key=%016jx rt=%02x\n", 932a276dc6bSMatthew Dillon (uintmax_t)mrec->skip.skip_beg.obj_id, 933a276dc6bSMatthew Dillon (uintmax_t)mrec->skip.skip_beg.key, 93417dd83bcSMatthew Dillon mrec->skip.skip_beg.rec_type, 935a276dc6bSMatthew Dillon (uintmax_t)mrec->skip.skip_end.obj_id, 936a276dc6bSMatthew Dillon (uintmax_t)mrec->skip.skip_end.key, 93717dd83bcSMatthew Dillon mrec->skip.skip_end.rec_type); 93817dd83bcSMatthew Dillon default: 93917dd83bcSMatthew Dillon break; 94017dd83bcSMatthew Dillon } 94117dd83bcSMatthew Dillon offset += bytes; 942243ca327SMatthew Dillon } 943243ca327SMatthew Dillon } 944243ca327SMatthew Dillon 945243ca327SMatthew Dillon /* 946243ca327SMatthew Dillon * Read and process the termination sync record. 947243ca327SMatthew Dillon */ 948243ca327SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 94948eadef9SMatthew Dillon if (mrec == NULL || 95048eadef9SMatthew Dillon (mrec->head.type != HAMMER_MREC_TYPE_SYNC && 95148eadef9SMatthew Dillon mrec->head.type != HAMMER_MREC_TYPE_IDLE) 95248eadef9SMatthew Dillon ) { 953243ca327SMatthew Dillon fprintf(stderr, "Mirror-dump: Did not get termination " 954243ca327SMatthew Dillon "sync record\n"); 955243ca327SMatthew Dillon } 956a7fbbf91SMatthew Dillon } 957a7fbbf91SMatthew Dillon 958a7fbbf91SMatthew Dillon void 95948eadef9SMatthew Dillon hammer_cmd_mirror_copy(char **av, int ac, int streaming) 960a7fbbf91SMatthew Dillon { 96134ebae70SMatthew Dillon pid_t pid1; 96234ebae70SMatthew Dillon pid_t pid2; 96334ebae70SMatthew Dillon int fds[2]; 964243ca327SMatthew Dillon const char *xav[16]; 965243ca327SMatthew Dillon char tbuf[16]; 96634ebae70SMatthew Dillon char *ptr; 967243ca327SMatthew Dillon int xac; 96834ebae70SMatthew Dillon 96934ebae70SMatthew Dillon if (ac != 2) 97034ebae70SMatthew Dillon mirror_usage(1); 97134ebae70SMatthew Dillon 972e7f926a5SMatthew Dillon TwoWayPipeOpt = 1; 9730bd7a37cSMatthew Dillon signal(SIGPIPE, SIG_IGN); 974e7f926a5SMatthew Dillon 975e7f926a5SMatthew Dillon again: 97634ebae70SMatthew Dillon if (pipe(fds) < 0) { 97734ebae70SMatthew Dillon perror("pipe"); 97834ebae70SMatthew Dillon exit(1); 97934ebae70SMatthew Dillon } 98034ebae70SMatthew Dillon 98134ebae70SMatthew Dillon /* 98234ebae70SMatthew Dillon * Source 98334ebae70SMatthew Dillon */ 98434ebae70SMatthew Dillon if ((pid1 = fork()) == 0) { 9850bd7a37cSMatthew Dillon signal(SIGPIPE, SIG_DFL); 98634ebae70SMatthew Dillon dup2(fds[0], 0); 98734ebae70SMatthew Dillon dup2(fds[0], 1); 98834ebae70SMatthew Dillon close(fds[0]); 98934ebae70SMatthew Dillon close(fds[1]); 99034ebae70SMatthew Dillon if ((ptr = strchr(av[0], ':')) != NULL) { 99134ebae70SMatthew Dillon *ptr++ = 0; 992243ca327SMatthew Dillon xac = 0; 993243ca327SMatthew Dillon xav[xac++] = "ssh"; 9943a998207SMatthew Dillon if (CompressOpt) 9953a998207SMatthew Dillon xav[xac++] = "-C"; 9966c45ca3eSMatthew Dillon if (SshPort) { 9976c45ca3eSMatthew Dillon xav[xac++] = "-p"; 9986c45ca3eSMatthew Dillon xav[xac++] = SshPort; 9996c45ca3eSMatthew Dillon } 1000243ca327SMatthew Dillon xav[xac++] = av[0]; 1001243ca327SMatthew Dillon xav[xac++] = "hammer"; 100248eadef9SMatthew Dillon 100348eadef9SMatthew Dillon switch(VerboseOpt) { 100448eadef9SMatthew Dillon case 0: 100548eadef9SMatthew Dillon break; 100648eadef9SMatthew Dillon case 1: 1007243ca327SMatthew Dillon xav[xac++] = "-v"; 100848eadef9SMatthew Dillon break; 100948eadef9SMatthew Dillon case 2: 101048eadef9SMatthew Dillon xav[xac++] = "-vv"; 101148eadef9SMatthew Dillon break; 101248eadef9SMatthew Dillon default: 101348eadef9SMatthew Dillon xav[xac++] = "-vvv"; 101448eadef9SMatthew Dillon break; 101548eadef9SMatthew Dillon } 101607485271SMichael Neumann if (ForceYesOpt) { 101707485271SMichael Neumann xav[xac++] = "-y"; 101807485271SMichael Neumann } 1019243ca327SMatthew Dillon xav[xac++] = "-2"; 1020243ca327SMatthew Dillon if (TimeoutOpt) { 1021243ca327SMatthew Dillon snprintf(tbuf, sizeof(tbuf), "%d", TimeoutOpt); 1022243ca327SMatthew Dillon xav[xac++] = "-t"; 1023243ca327SMatthew Dillon xav[xac++] = tbuf; 1024243ca327SMatthew Dillon } 102548eadef9SMatthew Dillon if (streaming) 1026901f434aSMatthew Dillon xav[xac++] = "mirror-read-stream"; 102748eadef9SMatthew Dillon else 1028243ca327SMatthew Dillon xav[xac++] = "mirror-read"; 1029243ca327SMatthew Dillon xav[xac++] = ptr; 1030243ca327SMatthew Dillon xav[xac++] = NULL; 1031243ca327SMatthew Dillon execv("/usr/bin/ssh", (void *)xav); 103234ebae70SMatthew Dillon } else { 103348eadef9SMatthew Dillon hammer_cmd_mirror_read(av, 1, streaming); 1034243ca327SMatthew Dillon fflush(stdout); 1035243ca327SMatthew Dillon fflush(stderr); 103634ebae70SMatthew Dillon } 103753d93cc7SMatthew Dillon _exit(1); 103834ebae70SMatthew Dillon } 103934ebae70SMatthew Dillon 104034ebae70SMatthew Dillon /* 104134ebae70SMatthew Dillon * Target 104234ebae70SMatthew Dillon */ 104334ebae70SMatthew Dillon if ((pid2 = fork()) == 0) { 10440bd7a37cSMatthew Dillon signal(SIGPIPE, SIG_DFL); 104534ebae70SMatthew Dillon dup2(fds[1], 0); 104634ebae70SMatthew Dillon dup2(fds[1], 1); 104734ebae70SMatthew Dillon close(fds[0]); 104834ebae70SMatthew Dillon close(fds[1]); 104934ebae70SMatthew Dillon if ((ptr = strchr(av[1], ':')) != NULL) { 105034ebae70SMatthew Dillon *ptr++ = 0; 1051243ca327SMatthew Dillon xac = 0; 1052243ca327SMatthew Dillon xav[xac++] = "ssh"; 10533a998207SMatthew Dillon if (CompressOpt) 10543a998207SMatthew Dillon xav[xac++] = "-C"; 10556c45ca3eSMatthew Dillon if (SshPort) { 10566c45ca3eSMatthew Dillon xav[xac++] = "-p"; 10576c45ca3eSMatthew Dillon xav[xac++] = SshPort; 10586c45ca3eSMatthew Dillon } 1059243ca327SMatthew Dillon xav[xac++] = av[1]; 1060243ca327SMatthew Dillon xav[xac++] = "hammer"; 106148eadef9SMatthew Dillon 106248eadef9SMatthew Dillon switch(VerboseOpt) { 106348eadef9SMatthew Dillon case 0: 106448eadef9SMatthew Dillon break; 106548eadef9SMatthew Dillon case 1: 1066243ca327SMatthew Dillon xav[xac++] = "-v"; 106748eadef9SMatthew Dillon break; 106848eadef9SMatthew Dillon case 2: 106948eadef9SMatthew Dillon xav[xac++] = "-vv"; 107048eadef9SMatthew Dillon break; 107148eadef9SMatthew Dillon default: 107248eadef9SMatthew Dillon xav[xac++] = "-vvv"; 107348eadef9SMatthew Dillon break; 107448eadef9SMatthew Dillon } 107507485271SMichael Neumann if (ForceYesOpt) { 107607485271SMichael Neumann xav[xac++] = "-y"; 107707485271SMichael Neumann } 1078243ca327SMatthew Dillon xav[xac++] = "-2"; 1079243ca327SMatthew Dillon xav[xac++] = "mirror-write"; 1080243ca327SMatthew Dillon xav[xac++] = ptr; 1081243ca327SMatthew Dillon xav[xac++] = NULL; 1082243ca327SMatthew Dillon execv("/usr/bin/ssh", (void *)xav); 108334ebae70SMatthew Dillon } else { 108434ebae70SMatthew Dillon hammer_cmd_mirror_write(av + 1, 1); 1085243ca327SMatthew Dillon fflush(stdout); 1086243ca327SMatthew Dillon fflush(stderr); 108734ebae70SMatthew Dillon } 108853d93cc7SMatthew Dillon _exit(1); 108934ebae70SMatthew Dillon } 109034ebae70SMatthew Dillon close(fds[0]); 109134ebae70SMatthew Dillon close(fds[1]); 109234ebae70SMatthew Dillon 109334ebae70SMatthew Dillon while (waitpid(pid1, NULL, 0) <= 0) 109434ebae70SMatthew Dillon ; 109534ebae70SMatthew Dillon while (waitpid(pid2, NULL, 0) <= 0) 109634ebae70SMatthew Dillon ; 1097e7f926a5SMatthew Dillon 1098e7f926a5SMatthew Dillon /* 1099e7f926a5SMatthew Dillon * If the link is lost restart 1100e7f926a5SMatthew Dillon */ 1101e7f926a5SMatthew Dillon if (streaming) { 1102e7f926a5SMatthew Dillon if (VerboseOpt) { 1103e7f926a5SMatthew Dillon fprintf(stderr, "\nLost Link\n"); 1104e7f926a5SMatthew Dillon fflush(stderr); 1105e7f926a5SMatthew Dillon } 11060bd7a37cSMatthew Dillon sleep(15 + DelayOpt); 1107e7f926a5SMatthew Dillon goto again; 1108e7f926a5SMatthew Dillon } 1109e7f926a5SMatthew Dillon 1110a7fbbf91SMatthew Dillon } 1111a7fbbf91SMatthew Dillon 1112243ca327SMatthew Dillon /* 1113243ca327SMatthew Dillon * Read and return multiple mrecords 1114243ca327SMatthew Dillon */ 1115a7fbbf91SMatthew Dillon static int 111617dd83bcSMatthew Dillon read_mrecords(int fd, char *buf, u_int size, hammer_ioc_mrecord_head_t pickup) 1117a7fbbf91SMatthew Dillon { 111817dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 1119a7fbbf91SMatthew Dillon u_int count; 1120a7fbbf91SMatthew Dillon size_t n; 1121a7fbbf91SMatthew Dillon size_t i; 112217dd83bcSMatthew Dillon size_t bytes; 112385a8e8a7SMatthew Dillon int type; 1124a7fbbf91SMatthew Dillon 1125a7fbbf91SMatthew Dillon count = 0; 1126a7fbbf91SMatthew Dillon while (size - count >= HAMMER_MREC_HEADSIZE) { 1127a7fbbf91SMatthew Dillon /* 1128a7fbbf91SMatthew Dillon * Cached the record header in case we run out of buffer 1129a7fbbf91SMatthew Dillon * space. 1130a7fbbf91SMatthew Dillon */ 113117dd83bcSMatthew Dillon fflush(stdout); 1132a7fbbf91SMatthew Dillon if (pickup->signature == 0) { 1133a7fbbf91SMatthew Dillon for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) { 1134a7fbbf91SMatthew Dillon i = read(fd, (char *)pickup + n, 1135a7fbbf91SMatthew Dillon HAMMER_MREC_HEADSIZE - n); 1136a7fbbf91SMatthew Dillon if (i <= 0) 1137a7fbbf91SMatthew Dillon break; 1138a7fbbf91SMatthew Dillon } 1139a7fbbf91SMatthew Dillon if (n == 0) 1140a7fbbf91SMatthew Dillon break; 1141a7fbbf91SMatthew Dillon if (n != HAMMER_MREC_HEADSIZE) { 1142a7fbbf91SMatthew Dillon fprintf(stderr, "read_mrecords: short read on pipe\n"); 1143a7fbbf91SMatthew Dillon exit(1); 1144a7fbbf91SMatthew Dillon } 1145a7fbbf91SMatthew Dillon if (pickup->signature != HAMMER_IOC_MIRROR_SIGNATURE) { 114634bb69d8SThomas Nikolajsen fprintf(stderr, "read_mrecords: malformed record on pipe, " 114734bb69d8SThomas Nikolajsen "bad signature\n"); 1148a7fbbf91SMatthew Dillon exit(1); 1149a7fbbf91SMatthew Dillon } 1150a7fbbf91SMatthew Dillon } 1151a7fbbf91SMatthew Dillon if (pickup->rec_size < HAMMER_MREC_HEADSIZE || 115217dd83bcSMatthew Dillon pickup->rec_size > sizeof(*mrec) + HAMMER_XBUFSIZE) { 115334bb69d8SThomas Nikolajsen fprintf(stderr, "read_mrecords: malformed record on pipe, " 115434bb69d8SThomas Nikolajsen "illegal rec_size\n"); 1155a7fbbf91SMatthew Dillon exit(1); 1156a7fbbf91SMatthew Dillon } 1157a7fbbf91SMatthew Dillon 1158a7fbbf91SMatthew Dillon /* 1159a7fbbf91SMatthew Dillon * Stop if we have insufficient space for the record and data. 1160a7fbbf91SMatthew Dillon */ 116117dd83bcSMatthew Dillon bytes = HAMMER_HEAD_DOALIGN(pickup->rec_size); 116217dd83bcSMatthew Dillon if (size - count < bytes) 1163a7fbbf91SMatthew Dillon break; 1164a7fbbf91SMatthew Dillon 1165a7fbbf91SMatthew Dillon /* 116685a8e8a7SMatthew Dillon * Stop if the record type is not a REC, SKIP, or PASS, 116785a8e8a7SMatthew Dillon * which are the only types the ioctl supports. Other types 116885a8e8a7SMatthew Dillon * are used only by the userland protocol. 116985a8e8a7SMatthew Dillon * 117085a8e8a7SMatthew Dillon * Ignore all flags. 1171243ca327SMatthew Dillon */ 117285a8e8a7SMatthew Dillon type = pickup->type & HAMMER_MRECF_TYPE_LOMASK; 117385a8e8a7SMatthew Dillon if (type != HAMMER_MREC_TYPE_PFSD && 117485a8e8a7SMatthew Dillon type != HAMMER_MREC_TYPE_REC && 117585a8e8a7SMatthew Dillon type != HAMMER_MREC_TYPE_SKIP && 117685a8e8a7SMatthew Dillon type != HAMMER_MREC_TYPE_PASS) { 1177243ca327SMatthew Dillon break; 117817dd83bcSMatthew Dillon } 1179243ca327SMatthew Dillon 1180243ca327SMatthew Dillon /* 1181a7fbbf91SMatthew Dillon * Read the remainder and clear the pickup signature. 1182a7fbbf91SMatthew Dillon */ 118317dd83bcSMatthew Dillon for (n = HAMMER_MREC_HEADSIZE; n < bytes; n += i) { 118417dd83bcSMatthew Dillon i = read(fd, buf + count + n, bytes - n); 1185a7fbbf91SMatthew Dillon if (i <= 0) 1186a7fbbf91SMatthew Dillon break; 1187a7fbbf91SMatthew Dillon } 118817dd83bcSMatthew Dillon if (n != bytes) { 1189a7fbbf91SMatthew Dillon fprintf(stderr, "read_mrecords: short read on pipe\n"); 1190a7fbbf91SMatthew Dillon exit(1); 1191a7fbbf91SMatthew Dillon } 119217dd83bcSMatthew Dillon 119317dd83bcSMatthew Dillon bcopy(pickup, buf + count, HAMMER_MREC_HEADSIZE); 119417dd83bcSMatthew Dillon pickup->signature = 0; 119517dd83bcSMatthew Dillon pickup->type = 0; 119617dd83bcSMatthew Dillon mrec = (void *)(buf + count); 119717dd83bcSMatthew Dillon 119817dd83bcSMatthew Dillon /* 119917dd83bcSMatthew Dillon * Validate the completed record 120017dd83bcSMatthew Dillon */ 120117dd83bcSMatthew Dillon if (mrec->head.rec_crc != 120217dd83bcSMatthew Dillon crc32((char *)mrec + HAMMER_MREC_CRCOFF, 120317dd83bcSMatthew Dillon mrec->head.rec_size - HAMMER_MREC_CRCOFF)) { 120417dd83bcSMatthew Dillon fprintf(stderr, "read_mrecords: malformed record " 120517dd83bcSMatthew Dillon "on pipe, bad crc\n"); 120617dd83bcSMatthew Dillon exit(1); 1207a7fbbf91SMatthew Dillon } 1208a7fbbf91SMatthew Dillon 120917dd83bcSMatthew Dillon /* 121085a8e8a7SMatthew Dillon * If its a B-Tree record validate the data crc. 121185a8e8a7SMatthew Dillon * 121285a8e8a7SMatthew Dillon * NOTE: If the VFS passes us an explicitly errorde mrec 121385a8e8a7SMatthew Dillon * we just pass it through. 121417dd83bcSMatthew Dillon */ 121585a8e8a7SMatthew Dillon type = mrec->head.type & HAMMER_MRECF_TYPE_MASK; 121685a8e8a7SMatthew Dillon 121785a8e8a7SMatthew Dillon if (type == HAMMER_MREC_TYPE_REC) { 121817dd83bcSMatthew Dillon if (mrec->head.rec_size < 121917dd83bcSMatthew Dillon sizeof(mrec->rec) + mrec->rec.leaf.data_len) { 122017dd83bcSMatthew Dillon fprintf(stderr, 122117dd83bcSMatthew Dillon "read_mrecords: malformed record on " 122217dd83bcSMatthew Dillon "pipe, illegal element data_len\n"); 122317dd83bcSMatthew Dillon exit(1); 122417dd83bcSMatthew Dillon } 122517dd83bcSMatthew Dillon if (mrec->rec.leaf.data_len && 122617dd83bcSMatthew Dillon mrec->rec.leaf.data_offset && 122717dd83bcSMatthew Dillon hammer_crc_test_leaf(&mrec->rec + 1, &mrec->rec.leaf) == 0) { 122817dd83bcSMatthew Dillon fprintf(stderr, 122917dd83bcSMatthew Dillon "read_mrecords: data_crc did not " 1230a276dc6bSMatthew Dillon "match data! obj=%016jx key=%016jx\n", 1231a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.obj_id, 1232a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.key); 123317dd83bcSMatthew Dillon fprintf(stderr, 123417dd83bcSMatthew Dillon "continuing, but there are problems\n"); 123517dd83bcSMatthew Dillon } 123617dd83bcSMatthew Dillon } 123717dd83bcSMatthew Dillon count += bytes; 1238a7fbbf91SMatthew Dillon } 1239a7fbbf91SMatthew Dillon return(count); 1240a7fbbf91SMatthew Dillon } 1241a7fbbf91SMatthew Dillon 124234ebae70SMatthew Dillon /* 124317dd83bcSMatthew Dillon * Read and return a single mrecord. 1244243ca327SMatthew Dillon */ 1245243ca327SMatthew Dillon static 124617dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t 124717dd83bcSMatthew Dillon read_mrecord(int fdin, int *errorp, hammer_ioc_mrecord_head_t pickup) 1248243ca327SMatthew Dillon { 124917dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 125017dd83bcSMatthew Dillon struct hammer_ioc_mrecord_head mrechd; 1251243ca327SMatthew Dillon size_t bytes; 1252243ca327SMatthew Dillon size_t n; 1253243ca327SMatthew Dillon size_t i; 1254243ca327SMatthew Dillon 1255243ca327SMatthew Dillon if (pickup && pickup->type != 0) { 1256243ca327SMatthew Dillon mrechd = *pickup; 1257243ca327SMatthew Dillon pickup->signature = 0; 1258243ca327SMatthew Dillon pickup->type = 0; 1259243ca327SMatthew Dillon n = HAMMER_MREC_HEADSIZE; 1260243ca327SMatthew Dillon } else { 1261243ca327SMatthew Dillon /* 1262243ca327SMatthew Dillon * Read in the PFSD header from the sender. 1263243ca327SMatthew Dillon */ 1264243ca327SMatthew Dillon for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) { 1265243ca327SMatthew Dillon i = read(fdin, (char *)&mrechd + n, HAMMER_MREC_HEADSIZE - n); 1266243ca327SMatthew Dillon if (i <= 0) 1267243ca327SMatthew Dillon break; 1268243ca327SMatthew Dillon } 1269243ca327SMatthew Dillon if (n == 0) { 1270243ca327SMatthew Dillon *errorp = 0; /* EOF */ 1271243ca327SMatthew Dillon return(NULL); 1272243ca327SMatthew Dillon } 1273243ca327SMatthew Dillon if (n != HAMMER_MREC_HEADSIZE) { 1274243ca327SMatthew Dillon fprintf(stderr, "short read of mrecord header\n"); 1275243ca327SMatthew Dillon *errorp = EPIPE; 1276243ca327SMatthew Dillon return(NULL); 1277243ca327SMatthew Dillon } 1278243ca327SMatthew Dillon } 1279243ca327SMatthew Dillon if (mrechd.signature != HAMMER_IOC_MIRROR_SIGNATURE) { 1280243ca327SMatthew Dillon fprintf(stderr, "read_mrecord: bad signature\n"); 1281243ca327SMatthew Dillon *errorp = EINVAL; 1282243ca327SMatthew Dillon return(NULL); 1283243ca327SMatthew Dillon } 128417dd83bcSMatthew Dillon bytes = HAMMER_HEAD_DOALIGN(mrechd.rec_size); 128517dd83bcSMatthew Dillon assert(bytes >= sizeof(mrechd)); 1286243ca327SMatthew Dillon mrec = malloc(bytes); 128717dd83bcSMatthew Dillon mrec->head = mrechd; 128817dd83bcSMatthew Dillon 1289243ca327SMatthew Dillon while (n < bytes) { 1290243ca327SMatthew Dillon i = read(fdin, (char *)mrec + n, bytes - n); 1291243ca327SMatthew Dillon if (i <= 0) 1292243ca327SMatthew Dillon break; 1293243ca327SMatthew Dillon n += i; 1294243ca327SMatthew Dillon } 1295243ca327SMatthew Dillon if (n != bytes) { 1296243ca327SMatthew Dillon fprintf(stderr, "read_mrecord: short read on payload\n"); 1297243ca327SMatthew Dillon *errorp = EPIPE; 1298243ca327SMatthew Dillon return(NULL); 1299243ca327SMatthew Dillon } 130017dd83bcSMatthew Dillon if (mrec->head.rec_crc != 130117dd83bcSMatthew Dillon crc32((char *)mrec + HAMMER_MREC_CRCOFF, 130217dd83bcSMatthew Dillon mrec->head.rec_size - HAMMER_MREC_CRCOFF)) { 1303243ca327SMatthew Dillon fprintf(stderr, "read_mrecord: bad CRC\n"); 1304243ca327SMatthew Dillon *errorp = EINVAL; 1305243ca327SMatthew Dillon return(NULL); 1306243ca327SMatthew Dillon } 1307243ca327SMatthew Dillon *errorp = 0; 1308243ca327SMatthew Dillon return(mrec); 1309243ca327SMatthew Dillon } 1310243ca327SMatthew Dillon 1311243ca327SMatthew Dillon static 1312243ca327SMatthew Dillon void 131317dd83bcSMatthew Dillon write_mrecord(int fdout, u_int32_t type, hammer_ioc_mrecord_any_t mrec, 131417dd83bcSMatthew Dillon int bytes) 1315243ca327SMatthew Dillon { 131617dd83bcSMatthew Dillon char zbuf[HAMMER_HEAD_ALIGN]; 131717dd83bcSMatthew Dillon int pad; 1318243ca327SMatthew Dillon 131917dd83bcSMatthew Dillon pad = HAMMER_HEAD_DOALIGN(bytes) - bytes; 132017dd83bcSMatthew Dillon 132117dd83bcSMatthew Dillon assert(bytes >= (int)sizeof(mrec->head)); 132217dd83bcSMatthew Dillon bzero(&mrec->head, sizeof(mrec->head)); 132317dd83bcSMatthew Dillon mrec->head.signature = HAMMER_IOC_MIRROR_SIGNATURE; 132417dd83bcSMatthew Dillon mrec->head.type = type; 132517dd83bcSMatthew Dillon mrec->head.rec_size = bytes; 132617dd83bcSMatthew Dillon mrec->head.rec_crc = crc32((char *)mrec + HAMMER_MREC_CRCOFF, 132717dd83bcSMatthew Dillon bytes - HAMMER_MREC_CRCOFF); 132817dd83bcSMatthew Dillon if (write(fdout, mrec, bytes) != bytes) { 1329243ca327SMatthew Dillon fprintf(stderr, "write_mrecord: error %d (%s)\n", 1330243ca327SMatthew Dillon errno, strerror(errno)); 1331243ca327SMatthew Dillon exit(1); 1332243ca327SMatthew Dillon } 133317dd83bcSMatthew Dillon if (pad) { 133417dd83bcSMatthew Dillon bzero(zbuf, pad); 133517dd83bcSMatthew Dillon if (write(fdout, zbuf, pad) != pad) { 133617dd83bcSMatthew Dillon fprintf(stderr, "write_mrecord: error %d (%s)\n", 133717dd83bcSMatthew Dillon errno, strerror(errno)); 133817dd83bcSMatthew Dillon exit(1); 133917dd83bcSMatthew Dillon } 134017dd83bcSMatthew Dillon } 1341243ca327SMatthew Dillon } 1342243ca327SMatthew Dillon 1343243ca327SMatthew Dillon /* 134434ebae70SMatthew Dillon * Generate a mirroring header with the pfs information of the 134534ebae70SMatthew Dillon * originating filesytem. 134634ebae70SMatthew Dillon */ 134734ebae70SMatthew Dillon static void 1348e7f926a5SMatthew Dillon generate_mrec_header(int fd, int pfs_id, 1349e7f926a5SMatthew Dillon union hammer_ioc_mrecord_any *mrec_tmp) 135034ebae70SMatthew Dillon { 135134ebae70SMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 135234ebae70SMatthew Dillon 135334ebae70SMatthew Dillon bzero(&pfs, sizeof(pfs)); 1354e7f926a5SMatthew Dillon bzero(mrec_tmp, sizeof(*mrec_tmp)); 1355d4e5b69bSMatthew Dillon pfs.pfs_id = pfs_id; 1356e7f926a5SMatthew Dillon pfs.ondisk = &mrec_tmp->pfs.pfsd; 1357e7f926a5SMatthew Dillon pfs.bytes = sizeof(mrec_tmp->pfs.pfsd); 135834ebae70SMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 13599dc76cb1SMatthew Dillon fprintf(stderr, "Mirror-read: not a HAMMER fs/pseudofs!\n"); 136034ebae70SMatthew Dillon exit(1); 136134ebae70SMatthew Dillon } 136234ebae70SMatthew Dillon if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) { 13639dc76cb1SMatthew Dillon fprintf(stderr, "Mirror-read: HAMMER pfs version mismatch!\n"); 136434ebae70SMatthew Dillon exit(1); 136534ebae70SMatthew Dillon } 1366e7f926a5SMatthew Dillon mrec_tmp->pfs.version = pfs.version; 136734ebae70SMatthew Dillon } 136834ebae70SMatthew Dillon 136934ebae70SMatthew Dillon /* 137034ebae70SMatthew Dillon * Validate the pfs information from the originating filesystem 137134ebae70SMatthew Dillon * against the target filesystem. shared_uuid must match. 137248eadef9SMatthew Dillon * 137348eadef9SMatthew Dillon * return -1 if we got a TERM record 137434ebae70SMatthew Dillon */ 137548eadef9SMatthew Dillon static int 1376d4e5b69bSMatthew Dillon validate_mrec_header(int fd, int fdin, int is_target, int pfs_id, 137748eadef9SMatthew Dillon struct hammer_ioc_mrecord_head *pickup, 137834ebae70SMatthew Dillon hammer_tid_t *tid_begp, hammer_tid_t *tid_endp) 137934ebae70SMatthew Dillon { 138034ebae70SMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 138134ebae70SMatthew Dillon struct hammer_pseudofs_data pfsd; 138217dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 1383243ca327SMatthew Dillon int error; 138434ebae70SMatthew Dillon 138534ebae70SMatthew Dillon /* 138634ebae70SMatthew Dillon * Get the PFSD info from the target filesystem. 138734ebae70SMatthew Dillon */ 138834ebae70SMatthew Dillon bzero(&pfs, sizeof(pfs)); 138934ebae70SMatthew Dillon bzero(&pfsd, sizeof(pfsd)); 1390d4e5b69bSMatthew Dillon pfs.pfs_id = pfs_id; 139134ebae70SMatthew Dillon pfs.ondisk = &pfsd; 139234ebae70SMatthew Dillon pfs.bytes = sizeof(pfsd); 139334ebae70SMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 139434ebae70SMatthew Dillon fprintf(stderr, "mirror-write: not a HAMMER fs/pseudofs!\n"); 139534ebae70SMatthew Dillon exit(1); 139634ebae70SMatthew Dillon } 139734ebae70SMatthew Dillon if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) { 139834ebae70SMatthew Dillon fprintf(stderr, "mirror-write: HAMMER pfs version mismatch!\n"); 139934ebae70SMatthew Dillon exit(1); 140034ebae70SMatthew Dillon } 140134ebae70SMatthew Dillon 140248eadef9SMatthew Dillon mrec = read_mrecord(fdin, &error, pickup); 1403243ca327SMatthew Dillon if (mrec == NULL) { 1404243ca327SMatthew Dillon if (error == 0) 1405243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: short read\n"); 140634ebae70SMatthew Dillon exit(1); 140734ebae70SMatthew Dillon } 140848eadef9SMatthew Dillon if (mrec->head.type == HAMMER_MREC_TYPE_TERM) { 140948eadef9SMatthew Dillon free(mrec); 141048eadef9SMatthew Dillon return(-1); 141148eadef9SMatthew Dillon } 141248eadef9SMatthew Dillon 141317dd83bcSMatthew Dillon if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) { 1414243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: did not get expected " 1415243ca327SMatthew Dillon "PFSD record type\n"); 141634ebae70SMatthew Dillon exit(1); 141734ebae70SMatthew Dillon } 141817dd83bcSMatthew Dillon if (mrec->head.rec_size != sizeof(mrec->pfs)) { 1419243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: unexpected payload " 1420243ca327SMatthew Dillon "size\n"); 142134ebae70SMatthew Dillon exit(1); 142234ebae70SMatthew Dillon } 142317dd83bcSMatthew Dillon if (mrec->pfs.version != pfs.version) { 1424243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: Version mismatch\n"); 142534ebae70SMatthew Dillon exit(1); 142634ebae70SMatthew Dillon } 142734ebae70SMatthew Dillon 142834ebae70SMatthew Dillon /* 142934ebae70SMatthew Dillon * Whew. Ok, is the read PFS info compatible with the target? 143034ebae70SMatthew Dillon */ 143117dd83bcSMatthew Dillon if (bcmp(&mrec->pfs.pfsd.shared_uuid, &pfsd.shared_uuid, 143217dd83bcSMatthew Dillon sizeof(pfsd.shared_uuid)) != 0) { 143317dd83bcSMatthew Dillon fprintf(stderr, 143417dd83bcSMatthew Dillon "mirror-write: source and target have " 1435f265b84fSSascha Wildner "different shared-uuid's!\n"); 143634ebae70SMatthew Dillon exit(1); 143734ebae70SMatthew Dillon } 1438d4e5b69bSMatthew Dillon if (is_target && 1439d4e5b69bSMatthew Dillon (pfsd.mirror_flags & HAMMER_PFSD_SLAVE) == 0) { 144034ebae70SMatthew Dillon fprintf(stderr, "mirror-write: target must be in slave mode\n"); 144134ebae70SMatthew Dillon exit(1); 144234ebae70SMatthew Dillon } 1443d4e5b69bSMatthew Dillon if (tid_begp) 144417dd83bcSMatthew Dillon *tid_begp = mrec->pfs.pfsd.sync_beg_tid; 1445d4e5b69bSMatthew Dillon if (tid_endp) 144617dd83bcSMatthew Dillon *tid_endp = mrec->pfs.pfsd.sync_end_tid; 1447243ca327SMatthew Dillon free(mrec); 144848eadef9SMatthew Dillon return(0); 144934ebae70SMatthew Dillon } 145034ebae70SMatthew Dillon 145134ebae70SMatthew Dillon static void 1452d4e5b69bSMatthew Dillon update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id) 145334ebae70SMatthew Dillon { 1454243ca327SMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 1455243ca327SMatthew Dillon struct hammer_pseudofs_data pfsd; 145634ebae70SMatthew Dillon 1457243ca327SMatthew Dillon bzero(&pfs, sizeof(pfs)); 1458243ca327SMatthew Dillon bzero(&pfsd, sizeof(pfsd)); 1459d4e5b69bSMatthew Dillon pfs.pfs_id = pfs_id; 1460243ca327SMatthew Dillon pfs.ondisk = &pfsd; 1461243ca327SMatthew Dillon pfs.bytes = sizeof(pfsd); 1462243ca327SMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 1463243ca327SMatthew Dillon perror("update_pfs_snapshot (read)"); 1464243ca327SMatthew Dillon exit(1); 146534ebae70SMatthew Dillon } 14669c67b4d2SMatthew Dillon if (pfsd.sync_end_tid != snapshot_tid) { 1467ddc8e722SMatthew Dillon pfsd.sync_end_tid = snapshot_tid; 1468243ca327SMatthew Dillon if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) != 0) { 1469243ca327SMatthew Dillon perror("update_pfs_snapshot (rewrite)"); 1470243ca327SMatthew Dillon exit(1); 147134ebae70SMatthew Dillon } 147248eadef9SMatthew Dillon if (VerboseOpt >= 2) { 14739dc76cb1SMatthew Dillon fprintf(stderr, 1474*ced4470dSMatthew Dillon "Mirror-write: Completed, updated snapshot " 1475a276dc6bSMatthew Dillon "to %016jx\n", 1476a276dc6bSMatthew Dillon (uintmax_t)snapshot_tid); 1477*ced4470dSMatthew Dillon fflush(stderr); 1478243ca327SMatthew Dillon } 14799c67b4d2SMatthew Dillon } 148048eadef9SMatthew Dillon } 1481243ca327SMatthew Dillon 148248eadef9SMatthew Dillon /* 148348eadef9SMatthew Dillon * Bandwidth-limited write in chunks 148448eadef9SMatthew Dillon */ 148548eadef9SMatthew Dillon static 148648eadef9SMatthew Dillon ssize_t 148748eadef9SMatthew Dillon writebw(int fd, const void *buf, size_t nbytes, 148848eadef9SMatthew Dillon u_int64_t *bwcount, struct timeval *tv1) 148948eadef9SMatthew Dillon { 149048eadef9SMatthew Dillon struct timeval tv2; 149148eadef9SMatthew Dillon size_t n; 149248eadef9SMatthew Dillon ssize_t r; 149348eadef9SMatthew Dillon ssize_t a; 149448eadef9SMatthew Dillon int usec; 149548eadef9SMatthew Dillon 149648eadef9SMatthew Dillon a = 0; 149748eadef9SMatthew Dillon r = 0; 149848eadef9SMatthew Dillon while (nbytes) { 149948eadef9SMatthew Dillon if (*bwcount + nbytes > BandwidthOpt) 150048eadef9SMatthew Dillon n = BandwidthOpt - *bwcount; 150148eadef9SMatthew Dillon else 150248eadef9SMatthew Dillon n = nbytes; 150348eadef9SMatthew Dillon if (n) 150448eadef9SMatthew Dillon r = write(fd, buf, n); 150548eadef9SMatthew Dillon if (r >= 0) { 150648eadef9SMatthew Dillon a += r; 150748eadef9SMatthew Dillon nbytes -= r; 150848eadef9SMatthew Dillon buf = (const char *)buf + r; 150948eadef9SMatthew Dillon } 151048eadef9SMatthew Dillon if ((size_t)r != n) 151148eadef9SMatthew Dillon break; 151248eadef9SMatthew Dillon *bwcount += n; 151348eadef9SMatthew Dillon if (*bwcount >= BandwidthOpt) { 151448eadef9SMatthew Dillon gettimeofday(&tv2, NULL); 151548eadef9SMatthew Dillon usec = (int)(tv2.tv_sec - tv1->tv_sec) * 1000000 + 151648eadef9SMatthew Dillon (int)(tv2.tv_usec - tv1->tv_usec); 151748eadef9SMatthew Dillon if (usec >= 0 && usec < 1000000) 151848eadef9SMatthew Dillon usleep(1000000 - usec); 151948eadef9SMatthew Dillon gettimeofday(tv1, NULL); 152048eadef9SMatthew Dillon *bwcount -= BandwidthOpt; 152148eadef9SMatthew Dillon } 152248eadef9SMatthew Dillon } 152348eadef9SMatthew Dillon return(a ? a : r); 152448eadef9SMatthew Dillon } 152534ebae70SMatthew Dillon 152601a72c9fSMatthew Dillon /* 152701a72c9fSMatthew Dillon * Get a yes or no answer from the terminal. The program may be run as 152801a72c9fSMatthew Dillon * part of a two-way pipe so we cannot use stdin for this operation. 152901a72c9fSMatthew Dillon */ 153001a72c9fSMatthew Dillon static int 153101a72c9fSMatthew Dillon getyn(void) 153201a72c9fSMatthew Dillon { 153301a72c9fSMatthew Dillon char buf[256]; 153401a72c9fSMatthew Dillon FILE *fp; 153501a72c9fSMatthew Dillon int result; 153601a72c9fSMatthew Dillon 153701a72c9fSMatthew Dillon fp = fopen("/dev/tty", "r"); 153801a72c9fSMatthew Dillon if (fp == NULL) { 153901a72c9fSMatthew Dillon fprintf(stderr, "No terminal for response\n"); 154001a72c9fSMatthew Dillon return(-1); 154101a72c9fSMatthew Dillon } 154201a72c9fSMatthew Dillon result = -1; 154301a72c9fSMatthew Dillon while (fgets(buf, sizeof(buf), fp) != NULL) { 154401a72c9fSMatthew Dillon if (buf[0] == 'y' || buf[0] == 'Y') { 154501a72c9fSMatthew Dillon result = 1; 154601a72c9fSMatthew Dillon break; 154701a72c9fSMatthew Dillon } 154801a72c9fSMatthew Dillon if (buf[0] == 'n' || buf[0] == 'N') { 154901a72c9fSMatthew Dillon result = 0; 155001a72c9fSMatthew Dillon break; 155101a72c9fSMatthew Dillon } 155201a72c9fSMatthew Dillon fprintf(stderr, "Response not understood\n"); 155301a72c9fSMatthew Dillon break; 155401a72c9fSMatthew Dillon } 155501a72c9fSMatthew Dillon fclose(fp); 155601a72c9fSMatthew Dillon return(result); 155701a72c9fSMatthew Dillon } 155801a72c9fSMatthew Dillon 1559a7fbbf91SMatthew Dillon static void 1560a7fbbf91SMatthew Dillon mirror_usage(int code) 1561a7fbbf91SMatthew Dillon { 1562a7fbbf91SMatthew Dillon fprintf(stderr, 156334bb69d8SThomas Nikolajsen "hammer mirror-read <filesystem> [begin-tid]\n" 156434bb69d8SThomas Nikolajsen "hammer mirror-read-stream <filesystem> [begin-tid]\n" 1565a7fbbf91SMatthew Dillon "hammer mirror-write <filesystem>\n" 1566243ca327SMatthew Dillon "hammer mirror-dump\n" 156734bb69d8SThomas Nikolajsen "hammer mirror-copy [[user@]host:]<filesystem>" 156834bb69d8SThomas Nikolajsen " [[user@]host:]<filesystem>\n" 156934bb69d8SThomas Nikolajsen "hammer mirror-stream [[user@]host:]<filesystem>" 157034bb69d8SThomas Nikolajsen " [[user@]host:]<filesystem>\n" 1571a7fbbf91SMatthew Dillon ); 1572a7fbbf91SMatthew Dillon exit(code); 1573a7fbbf91SMatthew Dillon } 157401a72c9fSMatthew Dillon 1575