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 */ 34a7fbbf91SMatthew Dillon 35a7fbbf91SMatthew Dillon #include "hammer.h" 36a7fbbf91SMatthew Dillon 37269cdd19SMatthew Dillon #define LINE1 0,20 38269cdd19SMatthew Dillon #define LINE2 20,78 39269cdd19SMatthew Dillon #define LINE3 90,70 40269cdd19SMatthew Dillon 41a7fbbf91SMatthew Dillon #define SERIALBUF_SIZE (512 * 1024) 42a7fbbf91SMatthew Dillon 43ced4470dSMatthew Dillon typedef struct histogram { 44ced4470dSMatthew Dillon hammer_tid_t tid; 45ced4470dSMatthew Dillon u_int64_t bytes; 46ced4470dSMatthew Dillon } *histogram_t; 47ced4470dSMatthew Dillon 48a7fbbf91SMatthew Dillon static int read_mrecords(int fd, char *buf, u_int size, 4917dd83bcSMatthew Dillon hammer_ioc_mrecord_head_t pickup); 50e7f926a5SMatthew Dillon static int generate_histogram(int fd, const char *filesystem, 51ced4470dSMatthew Dillon histogram_t *histogram_ary, 5239e88285SMatthew Dillon struct hammer_ioc_mirror_rw *mirror_base, 5339e88285SMatthew Dillon int *repeatp); 5417dd83bcSMatthew Dillon static hammer_ioc_mrecord_any_t read_mrecord(int fdin, int *errorp, 5517dd83bcSMatthew Dillon hammer_ioc_mrecord_head_t pickup); 5617dd83bcSMatthew Dillon static void write_mrecord(int fdout, u_int32_t type, 5717dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec, int bytes); 58e7f926a5SMatthew Dillon static void generate_mrec_header(int fd, int pfs_id, 59e7f926a5SMatthew Dillon union hammer_ioc_mrecord_any *mrec_tmp); 6048eadef9SMatthew Dillon static int validate_mrec_header(int fd, int fdin, int is_target, int pfs_id, 6148eadef9SMatthew Dillon struct hammer_ioc_mrecord_head *pickup, 6234ebae70SMatthew Dillon hammer_tid_t *tid_begp, hammer_tid_t *tid_endp); 63d4e5b69bSMatthew Dillon static void update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id); 6448eadef9SMatthew Dillon static ssize_t writebw(int fd, const void *buf, size_t nbytes, 6548eadef9SMatthew Dillon u_int64_t *bwcount, struct timeval *tv1); 6601a72c9fSMatthew Dillon static int getyn(void); 67a7fbbf91SMatthew Dillon static void mirror_usage(int code); 68a7fbbf91SMatthew Dillon 6917dd83bcSMatthew Dillon /* 7017dd83bcSMatthew Dillon * Generate a mirroring data stream from the specific source over the 7117dd83bcSMatthew Dillon * entire key range, but restricted to the specified transaction range. 7217dd83bcSMatthew Dillon * 7317dd83bcSMatthew Dillon * The HAMMER VFS does most of the work, we add a few new mrecord 7417dd83bcSMatthew Dillon * types to negotiate the TID ranges and verify that the entire 7517dd83bcSMatthew Dillon * stream made it to the destination. 7639e88285SMatthew Dillon * 7739e88285SMatthew Dillon * streaming will be 0 for mirror-read, 1 for mirror-stream. The code will 7839e88285SMatthew Dillon * set up a fake value of -1 when running the histogram for mirror-read. 7917dd83bcSMatthew Dillon */ 80a7fbbf91SMatthew Dillon void 8148eadef9SMatthew Dillon hammer_cmd_mirror_read(char **av, int ac, int streaming) 82a7fbbf91SMatthew Dillon { 83a7fbbf91SMatthew Dillon struct hammer_ioc_mirror_rw mirror; 84d4e5b69bSMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 8517dd83bcSMatthew Dillon union hammer_ioc_mrecord_any mrec_tmp; 8648eadef9SMatthew Dillon struct hammer_ioc_mrecord_head pickup; 8717dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 88243ca327SMatthew Dillon hammer_tid_t sync_tid; 89ced4470dSMatthew Dillon histogram_t histogram_ary; 90a7fbbf91SMatthew Dillon const char *filesystem; 91a7fbbf91SMatthew Dillon char *buf = malloc(SERIALBUF_SIZE); 92243ca327SMatthew Dillon int interrupted = 0; 93243ca327SMatthew Dillon int error; 94a7fbbf91SMatthew Dillon int fd; 95243ca327SMatthew Dillon int n; 9648eadef9SMatthew Dillon int didwork; 97e7f926a5SMatthew Dillon int histogram; 98ced4470dSMatthew Dillon int histindex; 99ced4470dSMatthew Dillon int histmax; 10039e88285SMatthew Dillon int repeat = 0; 10139e88285SMatthew Dillon int sameline; 10248eadef9SMatthew Dillon int64_t total_bytes; 103243ca327SMatthew Dillon time_t base_t = time(NULL); 10448eadef9SMatthew Dillon struct timeval bwtv; 10548eadef9SMatthew Dillon u_int64_t bwcount; 106ced4470dSMatthew Dillon u_int64_t estbytes; 107a7fbbf91SMatthew Dillon 10834bb69d8SThomas Nikolajsen if (ac == 0 || ac > 2) 109a7fbbf91SMatthew Dillon mirror_usage(1); 110a7fbbf91SMatthew Dillon filesystem = av[0]; 111*69f5a58cSMatthew Dillon hammer_check_restrict(filesystem); 112a7fbbf91SMatthew Dillon 11348eadef9SMatthew Dillon pickup.signature = 0; 11448eadef9SMatthew Dillon pickup.type = 0; 115ced4470dSMatthew Dillon histogram = 0; 116ced4470dSMatthew Dillon histindex = 0; 117ced4470dSMatthew Dillon histmax = 0; 118e7f926a5SMatthew Dillon histogram_ary = NULL; 11939e88285SMatthew Dillon sameline = 0; 12048eadef9SMatthew Dillon 12148eadef9SMatthew Dillon again: 122a7fbbf91SMatthew Dillon bzero(&mirror, sizeof(mirror)); 123a7fbbf91SMatthew Dillon hammer_key_beg_init(&mirror.key_beg); 124a7fbbf91SMatthew Dillon hammer_key_end_init(&mirror.key_end); 125a7fbbf91SMatthew Dillon 126d4e5b69bSMatthew Dillon fd = getpfs(&pfs, filesystem); 127a7fbbf91SMatthew Dillon 128269cdd19SMatthew Dillon if (streaming >= 0) 129269cdd19SMatthew Dillon score_printf(LINE1, "Running"); 130269cdd19SMatthew Dillon 13139e88285SMatthew Dillon if (streaming >= 0 && VerboseOpt && VerboseOpt < 2) { 13239e88285SMatthew Dillon fprintf(stderr, "%cRunning \b\b", (sameline ? '\r' : '\n')); 13348eadef9SMatthew Dillon fflush(stderr); 13439e88285SMatthew Dillon sameline = 1; 13548eadef9SMatthew Dillon } 13639e88285SMatthew Dillon sameline = 1; 13748eadef9SMatthew Dillon total_bytes = 0; 13848eadef9SMatthew Dillon gettimeofday(&bwtv, NULL); 13948eadef9SMatthew Dillon bwcount = 0; 14048eadef9SMatthew Dillon 141243ca327SMatthew Dillon /* 142e7f926a5SMatthew Dillon * Send initial header for the purpose of determining the 143e7f926a5SMatthew Dillon * shared-uuid. 14401a72c9fSMatthew Dillon */ 145e7f926a5SMatthew Dillon generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp); 146e7f926a5SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_PFSD, 147e7f926a5SMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.pfs)); 14801a72c9fSMatthew Dillon 14901a72c9fSMatthew Dillon /* 150d4e5b69bSMatthew Dillon * In 2-way mode the target will send us a PFS info packet 151d4e5b69bSMatthew Dillon * first. Use the target's current snapshot TID as our default 152d4e5b69bSMatthew Dillon * begin TID. 153243ca327SMatthew Dillon */ 15448eadef9SMatthew Dillon if (TwoWayPipeOpt) { 15539e88285SMatthew Dillon mirror.tid_beg = 0; 15648eadef9SMatthew Dillon n = validate_mrec_header(fd, 0, 0, pfs.pfs_id, &pickup, 157d4e5b69bSMatthew Dillon NULL, &mirror.tid_beg); 15848eadef9SMatthew Dillon if (n < 0) { /* got TERM record */ 15948eadef9SMatthew Dillon relpfs(fd, &pfs); 16048eadef9SMatthew Dillon return; 16148eadef9SMatthew Dillon } 16248eadef9SMatthew Dillon ++mirror.tid_beg; 16339e88285SMatthew Dillon } else if (streaming && histogram) { 16439e88285SMatthew Dillon mirror.tid_beg = histogram_ary[histindex].tid + 1; 16539e88285SMatthew Dillon } else { 16639e88285SMatthew Dillon mirror.tid_beg = 0; 16748eadef9SMatthew Dillon } 168d4e5b69bSMatthew Dillon 169d4e5b69bSMatthew Dillon /* 170d4e5b69bSMatthew Dillon * Write out the PFS header, tid_beg will be updated if our PFS 171d4e5b69bSMatthew Dillon * has a larger begin sync. tid_end is set to the latest source 172d4e5b69bSMatthew Dillon * TID whos flush cycle has completed. 173d4e5b69bSMatthew Dillon */ 174e7f926a5SMatthew Dillon generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp); 175e7f926a5SMatthew Dillon if (mirror.tid_beg < mrec_tmp.pfs.pfsd.sync_beg_tid) 176e7f926a5SMatthew Dillon mirror.tid_beg = mrec_tmp.pfs.pfsd.sync_beg_tid; 177e7f926a5SMatthew Dillon mirror.tid_end = mrec_tmp.pfs.pfsd.sync_end_tid; 178e7f926a5SMatthew Dillon mirror.ubuf = buf; 179e7f926a5SMatthew Dillon mirror.size = SERIALBUF_SIZE; 180e7f926a5SMatthew Dillon mirror.pfs_id = pfs.pfs_id; 181e7f926a5SMatthew Dillon mirror.shared_uuid = pfs.ondisk->shared_uuid; 182d4e5b69bSMatthew Dillon 183d4e5b69bSMatthew Dillon /* 184e7f926a5SMatthew Dillon * XXX If the histogram is exhausted and the TID delta is large 185e7f926a5SMatthew Dillon * the stream might have been offline for a while and is 186e7f926a5SMatthew Dillon * now picking it up again. Do another histogram. 187d4e5b69bSMatthew Dillon */ 188e7f926a5SMatthew Dillon #if 0 18939e88285SMatthew Dillon if (streaming && histogram && histindex == histend) { 190e7f926a5SMatthew Dillon if (mirror.tid_end - mirror.tid_beg > BULK_MINIMUM) 191ced4470dSMatthew Dillon histogram = 0; 192e7f926a5SMatthew Dillon } 193e7f926a5SMatthew Dillon #endif 19434ebae70SMatthew Dillon 195e7f926a5SMatthew Dillon /* 196e7f926a5SMatthew Dillon * Initial bulk startup control, try to do some incremental 197e7f926a5SMatthew Dillon * mirroring in order to allow the stream to be killed and 198e7f926a5SMatthew Dillon * restarted without having to start over. 199e7f926a5SMatthew Dillon */ 200ced4470dSMatthew Dillon if (histogram == 0 && BulkOpt == 0) { 20139e88285SMatthew Dillon if (VerboseOpt && repeat == 0) { 202e7f926a5SMatthew Dillon fprintf(stderr, "\n"); 20339e88285SMatthew Dillon sameline = 0; 20439e88285SMatthew Dillon } 205ced4470dSMatthew Dillon histmax = generate_histogram(fd, filesystem, 20639e88285SMatthew Dillon &histogram_ary, &mirror, 20739e88285SMatthew Dillon &repeat); 208ced4470dSMatthew Dillon histindex = 0; 209ced4470dSMatthew Dillon histogram = 1; 21039e88285SMatthew Dillon 21139e88285SMatthew Dillon /* 21239e88285SMatthew Dillon * Just stream the histogram, then stop 21339e88285SMatthew Dillon */ 21439e88285SMatthew Dillon if (streaming == 0) 21539e88285SMatthew Dillon streaming = -1; 216e7f926a5SMatthew Dillon } 217e7f926a5SMatthew Dillon 21839e88285SMatthew Dillon if (streaming && histogram) { 219ced4470dSMatthew Dillon ++histindex; 220ced4470dSMatthew Dillon mirror.tid_end = histogram_ary[histindex].tid; 221ced4470dSMatthew Dillon estbytes = histogram_ary[histindex-1].bytes; 222e7f926a5SMatthew Dillon mrec_tmp.pfs.pfsd.sync_end_tid = mirror.tid_end; 223ced4470dSMatthew Dillon } else { 224ced4470dSMatthew Dillon estbytes = 0; 225e7f926a5SMatthew Dillon } 226e7f926a5SMatthew Dillon 227e7f926a5SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_PFSD, 228e7f926a5SMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.pfs)); 229e7f926a5SMatthew Dillon 230e7f926a5SMatthew Dillon /* 231e7f926a5SMatthew Dillon * A cycle file overrides the beginning TID only if we are 23239e88285SMatthew Dillon * not operating in two-way or histogram mode. 233e7f926a5SMatthew Dillon */ 23439e88285SMatthew Dillon if (TwoWayPipeOpt == 0 && histogram == 0) { 235e7f926a5SMatthew Dillon hammer_get_cycle(&mirror.key_beg, &mirror.tid_beg); 236e7f926a5SMatthew Dillon } 237e7f926a5SMatthew Dillon 238e7f926a5SMatthew Dillon /* 239e7f926a5SMatthew Dillon * An additional argument overrides the beginning TID regardless 240e7f926a5SMatthew Dillon * of what mode we are in. This is not recommending if operating 241e7f926a5SMatthew Dillon * in two-way mode. 242e7f926a5SMatthew Dillon */ 24317dd83bcSMatthew Dillon if (ac == 2) 24417dd83bcSMatthew Dillon mirror.tid_beg = strtoull(av[1], NULL, 0); 24517dd83bcSMatthew Dillon 24648eadef9SMatthew Dillon if (streaming == 0 || VerboseOpt >= 2) { 24748eadef9SMatthew Dillon fprintf(stderr, 248ced4470dSMatthew Dillon "Mirror-read: Mirror %016jx to %016jx", 249a276dc6bSMatthew Dillon (uintmax_t)mirror.tid_beg, (uintmax_t)mirror.tid_end); 250ced4470dSMatthew Dillon if (histogram) 251ced4470dSMatthew Dillon fprintf(stderr, " (bulk= %ju)", (uintmax_t)estbytes); 2523d7b2393SMatthew Dillon fprintf(stderr, "\n"); 2533d7b2393SMatthew Dillon fflush(stderr); 25448eadef9SMatthew Dillon } 255243ca327SMatthew Dillon if (mirror.key_beg.obj_id != (int64_t)HAMMER_MIN_OBJID) { 256a276dc6bSMatthew Dillon fprintf(stderr, "Mirror-read: Resuming at object %016jx\n", 257a276dc6bSMatthew Dillon (uintmax_t)mirror.key_beg.obj_id); 258243ca327SMatthew Dillon } 259243ca327SMatthew Dillon 260243ca327SMatthew Dillon /* 2619dc76cb1SMatthew Dillon * Nothing to do if begin equals end. 2629dc76cb1SMatthew Dillon */ 26348eadef9SMatthew Dillon if (mirror.tid_beg >= mirror.tid_end) { 26448eadef9SMatthew Dillon if (streaming == 0 || VerboseOpt >= 2) 26548eadef9SMatthew Dillon fprintf(stderr, "Mirror-read: No work to do\n"); 26639e88285SMatthew Dillon sleep(DelayOpt); 26748eadef9SMatthew Dillon didwork = 0; 26839e88285SMatthew Dillon histogram = 0; 2699dc76cb1SMatthew Dillon goto done; 2709dc76cb1SMatthew Dillon } 27148eadef9SMatthew Dillon didwork = 1; 2729dc76cb1SMatthew Dillon 2739dc76cb1SMatthew Dillon /* 274243ca327SMatthew Dillon * Write out bulk records 275243ca327SMatthew Dillon */ 276a7fbbf91SMatthew Dillon mirror.ubuf = buf; 277a7fbbf91SMatthew Dillon mirror.size = SERIALBUF_SIZE; 278a7fbbf91SMatthew Dillon 279a7fbbf91SMatthew Dillon do { 280a7fbbf91SMatthew Dillon mirror.count = 0; 281d4e5b69bSMatthew Dillon mirror.pfs_id = pfs.pfs_id; 282d4e5b69bSMatthew Dillon mirror.shared_uuid = pfs.ondisk->shared_uuid; 283a7fbbf91SMatthew Dillon if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) { 284269cdd19SMatthew Dillon score_printf(LINE3, "Mirror-read %s failed: %s", 285269cdd19SMatthew Dillon filesystem, strerror(errno)); 286a7fbbf91SMatthew Dillon fprintf(stderr, "Mirror-read %s failed: %s\n", 287a7fbbf91SMatthew Dillon filesystem, strerror(errno)); 288a7fbbf91SMatthew Dillon exit(1); 289a7fbbf91SMatthew Dillon } 2909c67b4d2SMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) { 291269cdd19SMatthew Dillon score_printf(LINE3, "Mirror-read %s fatal error %d", 292269cdd19SMatthew Dillon filesystem, mirror.head.error); 2939c67b4d2SMatthew Dillon fprintf(stderr, 2949c67b4d2SMatthew Dillon "Mirror-read %s fatal error %d\n", 2959c67b4d2SMatthew Dillon filesystem, mirror.head.error); 2969c67b4d2SMatthew Dillon exit(1); 2979c67b4d2SMatthew Dillon } 298243ca327SMatthew Dillon if (mirror.count) { 29948eadef9SMatthew Dillon if (BandwidthOpt) { 30048eadef9SMatthew Dillon n = writebw(1, mirror.ubuf, mirror.count, 30148eadef9SMatthew Dillon &bwcount, &bwtv); 30248eadef9SMatthew Dillon } else { 303243ca327SMatthew Dillon n = write(1, mirror.ubuf, mirror.count); 30448eadef9SMatthew Dillon } 305243ca327SMatthew Dillon if (n != mirror.count) { 306269cdd19SMatthew Dillon score_printf(LINE3, 307269cdd19SMatthew Dillon "Mirror-read %s failed: " 308269cdd19SMatthew Dillon "short write", 309269cdd19SMatthew Dillon filesystem); 310269cdd19SMatthew Dillon fprintf(stderr, 311269cdd19SMatthew Dillon "Mirror-read %s failed: " 312243ca327SMatthew Dillon "short write\n", 313243ca327SMatthew Dillon filesystem); 314243ca327SMatthew Dillon exit(1); 315243ca327SMatthew Dillon } 316a7fbbf91SMatthew Dillon } 31748eadef9SMatthew Dillon total_bytes += mirror.count; 31848eadef9SMatthew Dillon if (streaming && VerboseOpt) { 319e7f926a5SMatthew Dillon fprintf(stderr, 32039e88285SMatthew Dillon "\rscan obj=%016jx tids=%016jx:%016jx %11jd", 321a276dc6bSMatthew Dillon (uintmax_t)mirror.key_cur.obj_id, 322a276dc6bSMatthew Dillon (uintmax_t)mirror.tid_beg, 323a276dc6bSMatthew Dillon (uintmax_t)mirror.tid_end, 324a276dc6bSMatthew Dillon (intmax_t)total_bytes); 32548eadef9SMatthew Dillon fflush(stderr); 32639e88285SMatthew Dillon sameline = 0; 327269cdd19SMatthew Dillon } else if (streaming) { 328269cdd19SMatthew Dillon score_printf(LINE2, 329269cdd19SMatthew Dillon "obj=%016jx tids=%016jx:%016jx %11jd", 330269cdd19SMatthew Dillon (uintmax_t)mirror.key_cur.obj_id, 331269cdd19SMatthew Dillon (uintmax_t)mirror.tid_beg, 332269cdd19SMatthew Dillon (uintmax_t)mirror.tid_end, 333269cdd19SMatthew Dillon (intmax_t)total_bytes); 33448eadef9SMatthew Dillon } 335a7fbbf91SMatthew Dillon mirror.key_beg = mirror.key_cur; 336e7f926a5SMatthew Dillon 337e7f926a5SMatthew Dillon /* 338e7f926a5SMatthew Dillon * Deal with time limit option 339e7f926a5SMatthew Dillon */ 340243ca327SMatthew Dillon if (TimeoutOpt && 341243ca327SMatthew Dillon (unsigned)(time(NULL) - base_t) > (unsigned)TimeoutOpt) { 342269cdd19SMatthew Dillon score_printf(LINE3, 343269cdd19SMatthew Dillon "Mirror-read %s interrupted by timer at" 344269cdd19SMatthew Dillon " %016jx", 345269cdd19SMatthew Dillon filesystem, 346269cdd19SMatthew Dillon (uintmax_t)mirror.key_cur.obj_id); 347243ca327SMatthew Dillon fprintf(stderr, 348243ca327SMatthew Dillon "Mirror-read %s interrupted by timer at" 349a276dc6bSMatthew Dillon " %016jx\n", 350243ca327SMatthew Dillon filesystem, 351a276dc6bSMatthew Dillon (uintmax_t)mirror.key_cur.obj_id); 352243ca327SMatthew Dillon interrupted = 1; 353243ca327SMatthew Dillon break; 354243ca327SMatthew Dillon } 355a7fbbf91SMatthew Dillon } while (mirror.count != 0); 356a7fbbf91SMatthew Dillon 35748eadef9SMatthew Dillon done: 35839e88285SMatthew Dillon if (streaming && VerboseOpt && sameline == 0) { 359ced4470dSMatthew Dillon fprintf(stderr, "\n"); 360ced4470dSMatthew Dillon fflush(stderr); 36139e88285SMatthew Dillon sameline = 1; 362ced4470dSMatthew Dillon } 363ced4470dSMatthew Dillon 364243ca327SMatthew Dillon /* 36548eadef9SMatthew Dillon * Write out the termination sync record - only if not interrupted 366243ca327SMatthew Dillon */ 36748eadef9SMatthew Dillon if (interrupted == 0) { 36848eadef9SMatthew Dillon if (didwork) { 36917dd83bcSMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_SYNC, 37017dd83bcSMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.sync)); 37148eadef9SMatthew Dillon } else { 37248eadef9SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_IDLE, 37348eadef9SMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.sync)); 37448eadef9SMatthew Dillon } 37548eadef9SMatthew Dillon } 37634ebae70SMatthew Dillon 377243ca327SMatthew Dillon /* 378243ca327SMatthew Dillon * If the -2 option was given (automatic when doing mirror-copy), 379243ca327SMatthew Dillon * a two-way pipe is assumed and we expect a response mrec from 380243ca327SMatthew Dillon * the target. 381243ca327SMatthew Dillon */ 382243ca327SMatthew Dillon if (TwoWayPipeOpt) { 38348eadef9SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 38417dd83bcSMatthew Dillon if (mrec == NULL || 38517dd83bcSMatthew Dillon mrec->head.type != HAMMER_MREC_TYPE_UPDATE || 38617dd83bcSMatthew Dillon mrec->head.rec_size != sizeof(mrec->update)) { 387243ca327SMatthew Dillon fprintf(stderr, "mirror_read: Did not get final " 388243ca327SMatthew Dillon "acknowledgement packet from target\n"); 389243ca327SMatthew Dillon exit(1); 390243ca327SMatthew Dillon } 391243ca327SMatthew Dillon if (interrupted) { 392243ca327SMatthew Dillon if (CyclePath) { 393ced4470dSMatthew Dillon hammer_set_cycle(&mirror.key_cur, 394ced4470dSMatthew Dillon mirror.tid_beg); 39534bb69d8SThomas Nikolajsen fprintf(stderr, "Cyclefile %s updated for " 39634bb69d8SThomas Nikolajsen "continuation\n", CyclePath); 397243ca327SMatthew Dillon } 398243ca327SMatthew Dillon } else { 39917dd83bcSMatthew Dillon sync_tid = mrec->update.tid; 400243ca327SMatthew Dillon if (CyclePath) { 401243ca327SMatthew Dillon hammer_key_beg_init(&mirror.key_beg); 402243ca327SMatthew Dillon hammer_set_cycle(&mirror.key_beg, sync_tid); 403a276dc6bSMatthew Dillon fprintf(stderr, 404a276dc6bSMatthew Dillon "Cyclefile %s updated to 0x%016jx\n", 405a276dc6bSMatthew Dillon CyclePath, (uintmax_t)sync_tid); 406243ca327SMatthew Dillon } 407243ca327SMatthew Dillon } 408243ca327SMatthew Dillon } else if (CyclePath) { 409243ca327SMatthew Dillon /* NOTE! mirror.tid_beg cannot be updated */ 410243ca327SMatthew Dillon fprintf(stderr, "Warning: cycle file (-c option) cannot be " 411243ca327SMatthew Dillon "fully updated unless you use mirror-copy\n"); 412243ca327SMatthew Dillon hammer_set_cycle(&mirror.key_beg, mirror.tid_beg); 413243ca327SMatthew Dillon } 41448eadef9SMatthew Dillon if (streaming && interrupted == 0) { 41548eadef9SMatthew Dillon time_t t1 = time(NULL); 41648eadef9SMatthew Dillon time_t t2; 41748eadef9SMatthew Dillon 418e7f926a5SMatthew Dillon /* 41939e88285SMatthew Dillon * Try to break down large bulk transfers into smaller ones 42039e88285SMatthew Dillon * so it can sync the transaction id on the slave. This 42139e88285SMatthew Dillon * way if we get interrupted a restart doesn't have to 42239e88285SMatthew Dillon * start from scratch. 423e7f926a5SMatthew Dillon */ 42439e88285SMatthew Dillon if (streaming && histogram) { 425ced4470dSMatthew Dillon if (histindex != histmax) { 42639e88285SMatthew Dillon if (VerboseOpt && VerboseOpt < 2 && 42739e88285SMatthew Dillon streaming >= 0) { 428e7f926a5SMatthew Dillon fprintf(stderr, " (bulk incremental)"); 42939e88285SMatthew Dillon } 43039e88285SMatthew Dillon relpfs(fd, &pfs); 431e7f926a5SMatthew Dillon goto again; 432e7f926a5SMatthew Dillon } 433ced4470dSMatthew Dillon } 434e7f926a5SMatthew Dillon 43539e88285SMatthew Dillon if (VerboseOpt && streaming >= 0) { 43648eadef9SMatthew Dillon fprintf(stderr, " W"); 43748eadef9SMatthew Dillon fflush(stderr); 438269cdd19SMatthew Dillon } else if (streaming >= 0) { 439269cdd19SMatthew Dillon score_printf(LINE1, "Waiting"); 44048eadef9SMatthew Dillon } 44148eadef9SMatthew Dillon pfs.ondisk->sync_end_tid = mirror.tid_end; 44239e88285SMatthew Dillon if (streaming < 0) { 44339e88285SMatthew Dillon /* 44439e88285SMatthew Dillon * Fake streaming mode when using a histogram to 44539e88285SMatthew Dillon * break up a mirror-read, do not wait on source. 44639e88285SMatthew Dillon */ 44739e88285SMatthew Dillon streaming = 0; 44839e88285SMatthew Dillon } else if (ioctl(fd, HAMMERIOC_WAI_PSEUDOFS, &pfs) < 0) { 449269cdd19SMatthew Dillon score_printf(LINE3, 450269cdd19SMatthew Dillon "Mirror-read %s: cannot stream: %s\n", 451269cdd19SMatthew Dillon filesystem, strerror(errno)); 452269cdd19SMatthew Dillon fprintf(stderr, 453269cdd19SMatthew Dillon "Mirror-read %s: cannot stream: %s\n", 45448eadef9SMatthew Dillon filesystem, strerror(errno)); 45548eadef9SMatthew Dillon } else { 45648eadef9SMatthew Dillon t2 = time(NULL) - t1; 45748eadef9SMatthew Dillon if (t2 >= 0 && t2 < DelayOpt) { 45848eadef9SMatthew Dillon if (VerboseOpt) { 45948eadef9SMatthew Dillon fprintf(stderr, "\bD"); 46048eadef9SMatthew Dillon fflush(stderr); 46148eadef9SMatthew Dillon } 46248eadef9SMatthew Dillon sleep(DelayOpt - t2); 46348eadef9SMatthew Dillon } 46448eadef9SMatthew Dillon if (VerboseOpt) { 46548eadef9SMatthew Dillon fprintf(stderr, "\b "); 46648eadef9SMatthew Dillon fflush(stderr); 46748eadef9SMatthew Dillon } 46848eadef9SMatthew Dillon relpfs(fd, &pfs); 46948eadef9SMatthew Dillon goto again; 47048eadef9SMatthew Dillon } 47148eadef9SMatthew Dillon } 47248eadef9SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_TERM, 47348eadef9SMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.sync)); 47448eadef9SMatthew Dillon relpfs(fd, &pfs); 475a7fbbf91SMatthew Dillon fprintf(stderr, "Mirror-read %s succeeded\n", filesystem); 476a7fbbf91SMatthew Dillon } 477a7fbbf91SMatthew Dillon 478e7f926a5SMatthew Dillon /* 4793d7b2393SMatthew Dillon * What we are trying to do here is figure out how much data is 4803d7b2393SMatthew Dillon * going to be sent for the TID range and to break the TID range 4813d7b2393SMatthew Dillon * down into reasonably-sized slices (from the point of view of 4823d7b2393SMatthew Dillon * data sent) so a lost connection can restart at a reasonable 4833d7b2393SMatthew Dillon * place and not all the way back at the beginning. 484ced4470dSMatthew Dillon * 485ced4470dSMatthew Dillon * An entry's TID serves as the end_tid for the prior entry 486ced4470dSMatthew Dillon * So we have to offset the calculation by 1 so that TID falls into 487ced4470dSMatthew Dillon * the previous entry when populating entries. 488ced4470dSMatthew Dillon * 489ced4470dSMatthew Dillon * Because the transaction id space is bursty we need a relatively 490ced4470dSMatthew Dillon * large number of buckets (like a million) to do a reasonable job 491ced4470dSMatthew Dillon * for things like an initial bulk mirrors on a very large filesystem. 492e7f926a5SMatthew Dillon */ 493ced4470dSMatthew Dillon #define HIST_COUNT (1024 * 1024) 4943d7b2393SMatthew Dillon 495e7f926a5SMatthew Dillon static int 496e7f926a5SMatthew Dillon generate_histogram(int fd, const char *filesystem, 497ced4470dSMatthew Dillon histogram_t *histogram_ary, 49839e88285SMatthew Dillon struct hammer_ioc_mirror_rw *mirror_base, 49939e88285SMatthew Dillon int *repeatp) 500e7f926a5SMatthew Dillon { 501e7f926a5SMatthew Dillon struct hammer_ioc_mirror_rw mirror; 5023d7b2393SMatthew Dillon union hammer_ioc_mrecord_any *mrec; 503e7f926a5SMatthew Dillon hammer_tid_t tid_beg; 504e7f926a5SMatthew Dillon hammer_tid_t tid_end; 505ced4470dSMatthew Dillon hammer_tid_t tid; 506ced4470dSMatthew Dillon hammer_tid_t tidx; 507ced4470dSMatthew Dillon u_int64_t *tid_bytes; 5083d7b2393SMatthew Dillon u_int64_t total; 5093d7b2393SMatthew Dillon u_int64_t accum; 510269cdd19SMatthew Dillon int chunkno; 511e7f926a5SMatthew Dillon int i; 5123d7b2393SMatthew Dillon int res; 5133d7b2393SMatthew Dillon int off; 5143d7b2393SMatthew Dillon int len; 515e7f926a5SMatthew Dillon 516e7f926a5SMatthew Dillon mirror = *mirror_base; 517e7f926a5SMatthew Dillon tid_beg = mirror.tid_beg; 518e7f926a5SMatthew Dillon tid_end = mirror.tid_end; 5193d7b2393SMatthew Dillon mirror.head.flags |= HAMMER_IOC_MIRROR_NODATA; 520e7f926a5SMatthew Dillon 5213d7b2393SMatthew Dillon if (*histogram_ary == NULL) { 522ced4470dSMatthew Dillon *histogram_ary = malloc(sizeof(struct histogram) * 5233d7b2393SMatthew Dillon (HIST_COUNT + 2)); 5243d7b2393SMatthew Dillon } 5253d7b2393SMatthew Dillon if (tid_beg >= tid_end) 526e7f926a5SMatthew Dillon return(0); 527e7f926a5SMatthew Dillon 528ced4470dSMatthew Dillon /* needs 2 extra */ 529ced4470dSMatthew Dillon tid_bytes = malloc(sizeof(*tid_bytes) * (HIST_COUNT + 2)); 5304839c61eSSascha Wildner bzero(tid_bytes, sizeof(*tid_bytes) * (HIST_COUNT + 2)); 531ced4470dSMatthew Dillon 53239e88285SMatthew Dillon if (*repeatp == 0) { 5333d7b2393SMatthew Dillon fprintf(stderr, "Prescan to break up bulk transfer"); 5343d7b2393SMatthew Dillon if (VerboseOpt > 1) 5353d7b2393SMatthew Dillon fprintf(stderr, " (%juMB chunks)", 5363d7b2393SMatthew Dillon (uintmax_t)(SplitupOpt / (1024 * 1024))); 5373d7b2393SMatthew Dillon fprintf(stderr, "\n"); 53839e88285SMatthew Dillon } 539e7f926a5SMatthew Dillon 540ced4470dSMatthew Dillon /* 541ced4470dSMatthew Dillon * Note: (tid_beg,tid_end), range is inclusive of both beg & end. 542ced4470dSMatthew Dillon * 543ced4470dSMatthew Dillon * Note: Estimates can be off when the mirror is way behind due 544ced4470dSMatthew Dillon * to skips. 545ced4470dSMatthew Dillon */ 5463d7b2393SMatthew Dillon total = 0; 5473d7b2393SMatthew Dillon accum = 0; 548269cdd19SMatthew Dillon chunkno = 0; 5493d7b2393SMatthew Dillon for (;;) { 5503d7b2393SMatthew Dillon mirror.count = 0; 551e7f926a5SMatthew Dillon if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) { 552e7f926a5SMatthew Dillon fprintf(stderr, "Mirror-read %s failed: %s\n", 553e7f926a5SMatthew Dillon filesystem, strerror(errno)); 554e7f926a5SMatthew Dillon exit(1); 555e7f926a5SMatthew Dillon } 556e7f926a5SMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) { 557e7f926a5SMatthew Dillon fprintf(stderr, 558e7f926a5SMatthew Dillon "Mirror-read %s fatal error %d\n", 559e7f926a5SMatthew Dillon filesystem, mirror.head.error); 560e7f926a5SMatthew Dillon exit(1); 561e7f926a5SMatthew Dillon } 5623d7b2393SMatthew Dillon for (off = 0; 5633d7b2393SMatthew Dillon off < mirror.count; 5643d7b2393SMatthew Dillon off += HAMMER_HEAD_DOALIGN(mrec->head.rec_size) 5653d7b2393SMatthew Dillon ) { 5663d7b2393SMatthew Dillon mrec = (void *)((char *)mirror.ubuf + off); 567e7f926a5SMatthew Dillon 568e7f926a5SMatthew Dillon /* 569ced4470dSMatthew Dillon * We only care about general RECs and PASS 570ced4470dSMatthew Dillon * records. We ignore SKIPs. 571e7f926a5SMatthew Dillon */ 572ced4470dSMatthew Dillon switch (mrec->head.type & HAMMER_MRECF_TYPE_LOMASK) { 573ced4470dSMatthew Dillon case HAMMER_MREC_TYPE_REC: 574ced4470dSMatthew Dillon case HAMMER_MREC_TYPE_PASS: 575ced4470dSMatthew Dillon break; 576ced4470dSMatthew Dillon default: 577ced4470dSMatthew Dillon continue; 578e7f926a5SMatthew Dillon } 5793d7b2393SMatthew Dillon 580ced4470dSMatthew Dillon /* 581ced4470dSMatthew Dillon * Calculate for two indices, create_tid and 582ced4470dSMatthew Dillon * delete_tid. Record data only applies to 583ced4470dSMatthew Dillon * the create_tid. 584ced4470dSMatthew Dillon * 585ced4470dSMatthew Dillon * When tid is exactly on the boundary it really 586ced4470dSMatthew Dillon * belongs to the previous entry because scans 587ced4470dSMatthew Dillon * are inclusive of the ending entry. 588ced4470dSMatthew Dillon */ 589ced4470dSMatthew Dillon tid = mrec->rec.leaf.base.delete_tid; 590ced4470dSMatthew Dillon if (tid && tid >= tid_beg && tid <= tid_end) { 591ced4470dSMatthew Dillon len = HAMMER_HEAD_DOALIGN(mrec->head.rec_size); 592ced4470dSMatthew Dillon if (mrec->head.type == 593ced4470dSMatthew Dillon HAMMER_MREC_TYPE_REC) { 594ced4470dSMatthew Dillon len -= HAMMER_HEAD_DOALIGN( 595ced4470dSMatthew Dillon mrec->rec.leaf.data_len); 596ced4470dSMatthew Dillon assert(len > 0); 597ced4470dSMatthew Dillon } 598ced4470dSMatthew Dillon i = (tid - tid_beg) * HIST_COUNT / 5993d7b2393SMatthew Dillon (tid_end - tid_beg); 600ced4470dSMatthew Dillon tidx = tid_beg + i * (tid_end - tid_beg) / 601ced4470dSMatthew Dillon HIST_COUNT; 602ced4470dSMatthew Dillon if (tid == tidx && i) 603ced4470dSMatthew Dillon --i; 604ced4470dSMatthew Dillon assert(i >= 0 && i < HIST_COUNT); 6053d7b2393SMatthew Dillon tid_bytes[i] += len; 6063d7b2393SMatthew Dillon total += len; 6073d7b2393SMatthew Dillon accum += len; 6083d7b2393SMatthew Dillon } 609ced4470dSMatthew Dillon 610ced4470dSMatthew Dillon tid = mrec->rec.leaf.base.create_tid; 611ced4470dSMatthew Dillon if (tid && tid >= tid_beg && tid <= tid_end) { 612ced4470dSMatthew Dillon len = HAMMER_HEAD_DOALIGN(mrec->head.rec_size); 613ced4470dSMatthew Dillon if (mrec->head.type == 614ced4470dSMatthew Dillon HAMMER_MREC_TYPE_REC_NODATA) { 615ced4470dSMatthew Dillon len += HAMMER_HEAD_DOALIGN( 616ced4470dSMatthew Dillon mrec->rec.leaf.data_len); 617ced4470dSMatthew Dillon } 618ced4470dSMatthew Dillon i = (tid - tid_beg) * HIST_COUNT / 619ced4470dSMatthew Dillon (tid_end - tid_beg); 620ced4470dSMatthew Dillon tidx = tid_beg + i * (tid_end - tid_beg) / 621ced4470dSMatthew Dillon HIST_COUNT; 622ced4470dSMatthew Dillon if (tid == tidx && i) 623ced4470dSMatthew Dillon --i; 624ced4470dSMatthew Dillon assert(i >= 0 && i < HIST_COUNT); 625ced4470dSMatthew Dillon tid_bytes[i] += len; 626ced4470dSMatthew Dillon total += len; 627ced4470dSMatthew Dillon accum += len; 6283d7b2393SMatthew Dillon } 6293d7b2393SMatthew Dillon } 63039e88285SMatthew Dillon if (*repeatp == 0 && accum > SplitupOpt) { 631269cdd19SMatthew Dillon if (VerboseOpt > 1) { 6323d7b2393SMatthew Dillon fprintf(stderr, "."); 6333d7b2393SMatthew Dillon fflush(stderr); 6343d7b2393SMatthew Dillon } 635269cdd19SMatthew Dillon ++chunkno; 636269cdd19SMatthew Dillon score_printf(LINE2, "Prescan chunk %d", chunkno); 637269cdd19SMatthew Dillon accum = 0; 6383d7b2393SMatthew Dillon } 6393d7b2393SMatthew Dillon if (mirror.count == 0) 6403d7b2393SMatthew Dillon break; 6413d7b2393SMatthew Dillon mirror.key_beg = mirror.key_cur; 6423d7b2393SMatthew Dillon } 6433d7b2393SMatthew Dillon 6443d7b2393SMatthew Dillon /* 645d6c40a21SFrançois Tigeot * Reduce to SplitupOpt (default 4GB) chunks. This code may 646ced4470dSMatthew Dillon * use up to two additional elements. Do the array in-place. 647ced4470dSMatthew Dillon * 648ced4470dSMatthew Dillon * Inefficient degenerate cases can occur if we do not accumulate 649ced4470dSMatthew Dillon * at least the requested split amount, so error on the side of 650ced4470dSMatthew Dillon * going over a bit. 6513d7b2393SMatthew Dillon */ 6523d7b2393SMatthew Dillon res = 0; 653ced4470dSMatthew Dillon (*histogram_ary)[res].tid = tid_beg; 654ced4470dSMatthew Dillon (*histogram_ary)[res].bytes = tid_bytes[0]; 655ced4470dSMatthew Dillon for (i = 1; i < HIST_COUNT; ++i) { 656ced4470dSMatthew Dillon if ((*histogram_ary)[res].bytes >= SplitupOpt) { 657ced4470dSMatthew Dillon ++res; 658ced4470dSMatthew Dillon (*histogram_ary)[res].tid = tid_beg + 6593d7b2393SMatthew Dillon i * (tid_end - tid_beg) / 6603d7b2393SMatthew Dillon HIST_COUNT; 661ced4470dSMatthew Dillon (*histogram_ary)[res].bytes = 0; 662ced4470dSMatthew Dillon 6633d7b2393SMatthew Dillon } 664ced4470dSMatthew Dillon (*histogram_ary)[res].bytes += tid_bytes[i]; 6653d7b2393SMatthew Dillon } 666ced4470dSMatthew Dillon ++res; 667ced4470dSMatthew Dillon (*histogram_ary)[res].tid = tid_end; 668ced4470dSMatthew Dillon (*histogram_ary)[res].bytes = -1; 669ced4470dSMatthew Dillon 67039e88285SMatthew Dillon if (*repeatp == 0) { 6713d7b2393SMatthew Dillon if (VerboseOpt > 1) 6723d7b2393SMatthew Dillon fprintf(stderr, "\n"); /* newline after ... */ 673269cdd19SMatthew Dillon score_printf(LINE3, "Prescan %d chunks, total %ju MBytes", 674269cdd19SMatthew Dillon res, (uintmax_t)total / (1024 * 1024)); 67527eff55eSMatthew Dillon fprintf(stderr, "Prescan %d chunks, total %ju MBytes (", 6763d7b2393SMatthew Dillon res, (uintmax_t)total / (1024 * 1024)); 677ced4470dSMatthew Dillon for (i = 0; i < res && i < 3; ++i) { 678ced4470dSMatthew Dillon if (i) 679ced4470dSMatthew Dillon fprintf(stderr, ", "); 68039e88285SMatthew Dillon fprintf(stderr, "%ju", 68139e88285SMatthew Dillon (uintmax_t)(*histogram_ary)[i].bytes); 682ced4470dSMatthew Dillon } 683ced4470dSMatthew Dillon if (i < res) 684ced4470dSMatthew Dillon fprintf(stderr, ", ..."); 685ced4470dSMatthew Dillon fprintf(stderr, ")\n"); 68639e88285SMatthew Dillon } 68739e88285SMatthew Dillon assert(res <= HIST_COUNT); 68839e88285SMatthew Dillon *repeatp = 1; 689ced4470dSMatthew Dillon 690ced4470dSMatthew Dillon free(tid_bytes); 6913d7b2393SMatthew Dillon return(res); 692e7f926a5SMatthew Dillon } 693e7f926a5SMatthew Dillon 69401a72c9fSMatthew Dillon static void 69501a72c9fSMatthew Dillon create_pfs(const char *filesystem, uuid_t *s_uuid) 69601a72c9fSMatthew Dillon { 697f414d101SSascha Wildner if (ForceYesOpt == 1) { 69807485271SMichael Neumann fprintf(stderr, "PFS slave %s does not exist. " 69907485271SMichael Neumann "Auto create new slave PFS!\n", filesystem); 70007485271SMichael Neumann 701f414d101SSascha Wildner } else { 70201a72c9fSMatthew Dillon fprintf(stderr, "PFS slave %s does not exist.\n" 70301a72c9fSMatthew Dillon "Do you want to create a new slave PFS? (yes|no) ", 70401a72c9fSMatthew Dillon filesystem); 70501a72c9fSMatthew Dillon fflush(stderr); 70601a72c9fSMatthew Dillon if (getyn() != 1) { 70701a72c9fSMatthew Dillon fprintf(stderr, "Aborting operation\n"); 70801a72c9fSMatthew Dillon exit(1); 70901a72c9fSMatthew Dillon } 71007485271SMichael Neumann } 71101a72c9fSMatthew Dillon 71201a72c9fSMatthew Dillon u_int32_t status; 71301a72c9fSMatthew Dillon char *shared_uuid = NULL; 71401a72c9fSMatthew Dillon uuid_to_string(s_uuid, &shared_uuid, &status); 71501a72c9fSMatthew Dillon 71601a72c9fSMatthew Dillon char *cmd = NULL; 71701a72c9fSMatthew Dillon asprintf(&cmd, "/sbin/hammer pfs-slave '%s' shared-uuid=%s 1>&2", 71801a72c9fSMatthew Dillon filesystem, shared_uuid); 71901a72c9fSMatthew Dillon free(shared_uuid); 72001a72c9fSMatthew Dillon 72101a72c9fSMatthew Dillon if (cmd == NULL) { 72201a72c9fSMatthew Dillon fprintf(stderr, "Failed to alloc memory\n"); 72301a72c9fSMatthew Dillon exit(1); 72401a72c9fSMatthew Dillon } 72501a72c9fSMatthew Dillon if (system(cmd) != 0) { 72601a72c9fSMatthew Dillon fprintf(stderr, "Failed to create PFS\n"); 72701a72c9fSMatthew Dillon } 72801a72c9fSMatthew Dillon free(cmd); 72901a72c9fSMatthew Dillon } 73001a72c9fSMatthew Dillon 73117dd83bcSMatthew Dillon /* 73217dd83bcSMatthew Dillon * Pipe the mirroring data stream on stdin to the HAMMER VFS, adding 73317dd83bcSMatthew Dillon * some additional packet types to negotiate TID ranges and to verify 73417dd83bcSMatthew Dillon * completion. The HAMMER VFS does most of the work. 73517dd83bcSMatthew Dillon * 73617dd83bcSMatthew Dillon * It is important to note that the mirror.key_{beg,end} range must 73717dd83bcSMatthew Dillon * match the ranged used by the original. For now both sides use 73817dd83bcSMatthew Dillon * range the entire key space. 73917dd83bcSMatthew Dillon * 74017dd83bcSMatthew Dillon * It is even more important that the records in the stream conform 74117dd83bcSMatthew Dillon * to the TID range also supplied in the stream. The HAMMER VFS will 74217dd83bcSMatthew Dillon * use the REC, PASS, and SKIP record types to track the portions of 74317dd83bcSMatthew Dillon * the B-Tree being scanned in order to be able to proactively delete 74417dd83bcSMatthew Dillon * records on the target within those active areas that are not mentioned 74517dd83bcSMatthew Dillon * by the source. 74617dd83bcSMatthew Dillon * 74717dd83bcSMatthew Dillon * The mirror.key_cur field is used by the VFS to do this tracking. It 74817dd83bcSMatthew Dillon * must be initialized to key_beg but then is persistently updated by 74917dd83bcSMatthew Dillon * the HAMMER VFS on each successive ioctl() call. If you blow up this 75017dd83bcSMatthew Dillon * field you will blow up the mirror target, possibly to the point of 75117dd83bcSMatthew Dillon * deleting everything. As a safety measure the HAMMER VFS simply marks 75217dd83bcSMatthew Dillon * the records that the source has destroyed as deleted on the target, 75317dd83bcSMatthew Dillon * and normal pruning operations will deal with their final disposition 75417dd83bcSMatthew Dillon * at some later time. 75517dd83bcSMatthew Dillon */ 756a7fbbf91SMatthew Dillon void 757a7fbbf91SMatthew Dillon hammer_cmd_mirror_write(char **av, int ac) 758a7fbbf91SMatthew Dillon { 759a7fbbf91SMatthew Dillon struct hammer_ioc_mirror_rw mirror; 760a7fbbf91SMatthew Dillon const char *filesystem; 761a7fbbf91SMatthew Dillon char *buf = malloc(SERIALBUF_SIZE); 762d4e5b69bSMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 76317dd83bcSMatthew Dillon struct hammer_ioc_mrecord_head pickup; 764243ca327SMatthew Dillon struct hammer_ioc_synctid synctid; 76517dd83bcSMatthew Dillon union hammer_ioc_mrecord_any mrec_tmp; 76617dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 76701a72c9fSMatthew Dillon struct stat st; 768243ca327SMatthew Dillon int error; 769243ca327SMatthew Dillon int fd; 77048eadef9SMatthew Dillon int n; 771a7fbbf91SMatthew Dillon 77234bb69d8SThomas Nikolajsen if (ac != 1) 773a7fbbf91SMatthew Dillon mirror_usage(1); 774a7fbbf91SMatthew Dillon filesystem = av[0]; 775*69f5a58cSMatthew Dillon hammer_check_restrict(filesystem); 776a7fbbf91SMatthew Dillon 77748eadef9SMatthew Dillon pickup.signature = 0; 77848eadef9SMatthew Dillon pickup.type = 0; 77948eadef9SMatthew Dillon 78048eadef9SMatthew Dillon again: 781a7fbbf91SMatthew Dillon bzero(&mirror, sizeof(mirror)); 782a7fbbf91SMatthew Dillon hammer_key_beg_init(&mirror.key_beg); 783a7fbbf91SMatthew Dillon hammer_key_end_init(&mirror.key_end); 78417dd83bcSMatthew Dillon mirror.key_end = mirror.key_beg; 785a7fbbf91SMatthew Dillon 78601a72c9fSMatthew Dillon /* 78701a72c9fSMatthew Dillon * Read initial packet 78801a72c9fSMatthew Dillon */ 78901a72c9fSMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 79001a72c9fSMatthew Dillon if (mrec == NULL) { 79101a72c9fSMatthew Dillon if (error == 0) 79201a72c9fSMatthew Dillon fprintf(stderr, "validate_mrec_header: short read\n"); 79301a72c9fSMatthew Dillon exit(1); 79401a72c9fSMatthew Dillon } 79501a72c9fSMatthew Dillon /* 79601a72c9fSMatthew Dillon * Validate packet 79701a72c9fSMatthew Dillon */ 79801a72c9fSMatthew Dillon if (mrec->head.type == HAMMER_MREC_TYPE_TERM) { 79901a72c9fSMatthew Dillon return; 80001a72c9fSMatthew Dillon } 80101a72c9fSMatthew Dillon if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) { 80201a72c9fSMatthew Dillon fprintf(stderr, "validate_mrec_header: did not get expected " 80301a72c9fSMatthew Dillon "PFSD record type\n"); 80401a72c9fSMatthew Dillon exit(1); 80501a72c9fSMatthew Dillon } 80601a72c9fSMatthew Dillon if (mrec->head.rec_size != sizeof(mrec->pfs)) { 80701a72c9fSMatthew Dillon fprintf(stderr, "validate_mrec_header: unexpected payload " 80801a72c9fSMatthew Dillon "size\n"); 80901a72c9fSMatthew Dillon exit(1); 81001a72c9fSMatthew Dillon } 81101a72c9fSMatthew Dillon 81201a72c9fSMatthew Dillon /* 81301a72c9fSMatthew Dillon * Create slave PFS if it doesn't yet exist 81401a72c9fSMatthew Dillon */ 81501a72c9fSMatthew Dillon if (lstat(filesystem, &st) != 0) { 81601a72c9fSMatthew Dillon create_pfs(filesystem, &mrec->pfs.pfsd.shared_uuid); 81701a72c9fSMatthew Dillon } 81801a72c9fSMatthew Dillon free(mrec); 81901a72c9fSMatthew Dillon mrec = NULL; 82001a72c9fSMatthew Dillon 821d4e5b69bSMatthew Dillon fd = getpfs(&pfs, filesystem); 822a7fbbf91SMatthew Dillon 823243ca327SMatthew Dillon /* 824d4e5b69bSMatthew Dillon * In two-way mode the target writes out a PFS packet first. 825d4e5b69bSMatthew Dillon * The source uses our tid_end as its tid_beg by default, 826d4e5b69bSMatthew Dillon * picking up where it left off. 827243ca327SMatthew Dillon */ 828d4e5b69bSMatthew Dillon mirror.tid_beg = 0; 829d4e5b69bSMatthew Dillon if (TwoWayPipeOpt) { 830e7f926a5SMatthew Dillon generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp); 831e7f926a5SMatthew Dillon if (mirror.tid_beg < mrec_tmp.pfs.pfsd.sync_beg_tid) 832e7f926a5SMatthew Dillon mirror.tid_beg = mrec_tmp.pfs.pfsd.sync_beg_tid; 833e7f926a5SMatthew Dillon mirror.tid_end = mrec_tmp.pfs.pfsd.sync_end_tid; 834e7f926a5SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_PFSD, 835e7f926a5SMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.pfs)); 836d4e5b69bSMatthew Dillon } 837d4e5b69bSMatthew Dillon 838d4e5b69bSMatthew Dillon /* 83917dd83bcSMatthew Dillon * Read and process the PFS header. The source informs us of 84017dd83bcSMatthew Dillon * the TID range the stream represents. 841d4e5b69bSMatthew Dillon */ 84248eadef9SMatthew Dillon n = validate_mrec_header(fd, 0, 1, pfs.pfs_id, &pickup, 843d4e5b69bSMatthew Dillon &mirror.tid_beg, &mirror.tid_end); 84448eadef9SMatthew Dillon if (n < 0) { /* got TERM record */ 84548eadef9SMatthew Dillon relpfs(fd, &pfs); 84648eadef9SMatthew Dillon return; 84748eadef9SMatthew Dillon } 84834ebae70SMatthew Dillon 849a7fbbf91SMatthew Dillon mirror.ubuf = buf; 850a7fbbf91SMatthew Dillon mirror.size = SERIALBUF_SIZE; 851a7fbbf91SMatthew Dillon 852243ca327SMatthew Dillon /* 85317dd83bcSMatthew Dillon * Read and process bulk records (REC, PASS, and SKIP types). 85417dd83bcSMatthew Dillon * 85517dd83bcSMatthew Dillon * On your life, do NOT mess with mirror.key_cur or your mirror 85617dd83bcSMatthew Dillon * target may become history. 857243ca327SMatthew Dillon */ 858a7fbbf91SMatthew Dillon for (;;) { 859a7fbbf91SMatthew Dillon mirror.count = 0; 860d4e5b69bSMatthew Dillon mirror.pfs_id = pfs.pfs_id; 861d4e5b69bSMatthew Dillon mirror.shared_uuid = pfs.ondisk->shared_uuid; 862a7fbbf91SMatthew Dillon mirror.size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup); 863a7fbbf91SMatthew Dillon if (mirror.size <= 0) 864a7fbbf91SMatthew Dillon break; 865a7fbbf91SMatthew Dillon if (ioctl(fd, HAMMERIOC_MIRROR_WRITE, &mirror) < 0) { 866a7fbbf91SMatthew Dillon fprintf(stderr, "Mirror-write %s failed: %s\n", 867a7fbbf91SMatthew Dillon filesystem, strerror(errno)); 868a7fbbf91SMatthew Dillon exit(1); 869a7fbbf91SMatthew Dillon } 87017dd83bcSMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) { 87117dd83bcSMatthew Dillon fprintf(stderr, 87217dd83bcSMatthew Dillon "Mirror-write %s fatal error %d\n", 87317dd83bcSMatthew Dillon filesystem, mirror.head.error); 87417dd83bcSMatthew Dillon exit(1); 87517dd83bcSMatthew Dillon } 876243ca327SMatthew Dillon #if 0 877a7fbbf91SMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_INTR) { 878a7fbbf91SMatthew Dillon fprintf(stderr, 879a7fbbf91SMatthew Dillon "Mirror-write %s interrupted by timer at" 880243ca327SMatthew Dillon " %016llx\n", 881a7fbbf91SMatthew Dillon filesystem, 882243ca327SMatthew Dillon mirror.key_cur.obj_id); 883a7fbbf91SMatthew Dillon exit(0); 884a7fbbf91SMatthew Dillon } 885243ca327SMatthew Dillon #endif 886a7fbbf91SMatthew Dillon } 887243ca327SMatthew Dillon 888243ca327SMatthew Dillon /* 889243ca327SMatthew Dillon * Read and process the termination sync record. 890243ca327SMatthew Dillon */ 891243ca327SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 8929dc76cb1SMatthew Dillon 8939dc76cb1SMatthew Dillon if (mrec && mrec->head.type == HAMMER_MREC_TYPE_TERM) { 89448eadef9SMatthew Dillon fprintf(stderr, "Mirror-write: received termination request\n"); 89548eadef9SMatthew Dillon free(mrec); 8969dc76cb1SMatthew Dillon return; 8979dc76cb1SMatthew Dillon } 8989dc76cb1SMatthew Dillon 89917dd83bcSMatthew Dillon if (mrec == NULL || 90048eadef9SMatthew Dillon (mrec->head.type != HAMMER_MREC_TYPE_SYNC && 90148eadef9SMatthew Dillon mrec->head.type != HAMMER_MREC_TYPE_IDLE) || 90217dd83bcSMatthew Dillon mrec->head.rec_size != sizeof(mrec->sync)) { 903243ca327SMatthew Dillon fprintf(stderr, "Mirror-write %s: Did not get termination " 90417dd83bcSMatthew Dillon "sync record, or rec_size is wrong rt=%d\n", 905db7dd3cfSMatthew Dillon filesystem, 906a446fca6SSascha Wildner (mrec ? (int)mrec->head.type : -1)); 907da44aa75SMatthew Dillon exit(1); 908243ca327SMatthew Dillon } 909243ca327SMatthew Dillon 910243ca327SMatthew Dillon /* 911243ca327SMatthew Dillon * Update the PFS info on the target so the user has visibility 91248eadef9SMatthew Dillon * into the new snapshot, and sync the target filesystem. 913243ca327SMatthew Dillon */ 91448eadef9SMatthew Dillon if (mrec->head.type == HAMMER_MREC_TYPE_SYNC) { 915d4e5b69bSMatthew Dillon update_pfs_snapshot(fd, mirror.tid_end, pfs.pfs_id); 916243ca327SMatthew Dillon 917243ca327SMatthew Dillon bzero(&synctid, sizeof(synctid)); 918243ca327SMatthew Dillon synctid.op = HAMMER_SYNCTID_SYNC2; 919243ca327SMatthew Dillon ioctl(fd, HAMMERIOC_SYNCTID, &synctid); 920243ca327SMatthew Dillon 92148eadef9SMatthew Dillon if (VerboseOpt >= 2) { 92248eadef9SMatthew Dillon fprintf(stderr, "Mirror-write %s: succeeded\n", 92348eadef9SMatthew Dillon filesystem); 92448eadef9SMatthew Dillon } 92548eadef9SMatthew Dillon } 92648eadef9SMatthew Dillon 92748eadef9SMatthew Dillon free(mrec); 92848eadef9SMatthew Dillon mrec = NULL; 929243ca327SMatthew Dillon 930243ca327SMatthew Dillon /* 931243ca327SMatthew Dillon * Report back to the originator. 932243ca327SMatthew Dillon */ 933243ca327SMatthew Dillon if (TwoWayPipeOpt) { 93417dd83bcSMatthew Dillon mrec_tmp.update.tid = mirror.tid_end; 935243ca327SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_UPDATE, 93617dd83bcSMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.update)); 937243ca327SMatthew Dillon } else { 938a276dc6bSMatthew Dillon printf("Source can update synctid to 0x%016jx\n", 939a276dc6bSMatthew Dillon (uintmax_t)mirror.tid_end); 940243ca327SMatthew Dillon } 94148eadef9SMatthew Dillon relpfs(fd, &pfs); 94248eadef9SMatthew Dillon goto again; 943243ca327SMatthew Dillon } 944243ca327SMatthew Dillon 945243ca327SMatthew Dillon void 946243ca327SMatthew Dillon hammer_cmd_mirror_dump(void) 947243ca327SMatthew Dillon { 948243ca327SMatthew Dillon char *buf = malloc(SERIALBUF_SIZE); 94917dd83bcSMatthew Dillon struct hammer_ioc_mrecord_head pickup; 95017dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 951243ca327SMatthew Dillon int error; 952243ca327SMatthew Dillon int size; 95317dd83bcSMatthew Dillon int offset; 95417dd83bcSMatthew Dillon int bytes; 955243ca327SMatthew Dillon 956243ca327SMatthew Dillon /* 957243ca327SMatthew Dillon * Read and process the PFS header 958243ca327SMatthew Dillon */ 959243ca327SMatthew Dillon pickup.signature = 0; 960243ca327SMatthew Dillon pickup.type = 0; 961243ca327SMatthew Dillon 962243ca327SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 963243ca327SMatthew Dillon 96439e88285SMatthew Dillon again: 965243ca327SMatthew Dillon /* 966243ca327SMatthew Dillon * Read and process bulk records 967243ca327SMatthew Dillon */ 968243ca327SMatthew Dillon for (;;) { 969243ca327SMatthew Dillon size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup); 970243ca327SMatthew Dillon if (size <= 0) 971243ca327SMatthew Dillon break; 97217dd83bcSMatthew Dillon offset = 0; 97317dd83bcSMatthew Dillon while (offset < size) { 97417dd83bcSMatthew Dillon mrec = (void *)((char *)buf + offset); 97517dd83bcSMatthew Dillon bytes = HAMMER_HEAD_DOALIGN(mrec->head.rec_size); 97617dd83bcSMatthew Dillon if (offset + bytes > size) { 97717dd83bcSMatthew Dillon fprintf(stderr, "Misaligned record\n"); 97817dd83bcSMatthew Dillon exit(1); 97917dd83bcSMatthew Dillon } 98017dd83bcSMatthew Dillon 98185a8e8a7SMatthew Dillon switch(mrec->head.type & HAMMER_MRECF_TYPE_MASK) { 98285a8e8a7SMatthew Dillon case HAMMER_MREC_TYPE_REC_BADCRC: 98317dd83bcSMatthew Dillon case HAMMER_MREC_TYPE_REC: 984a276dc6bSMatthew Dillon printf("Record obj=%016jx key=%016jx " 98585a8e8a7SMatthew Dillon "rt=%02x ot=%02x", 986a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.obj_id, 987a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.key, 98817dd83bcSMatthew Dillon mrec->rec.leaf.base.rec_type, 98917dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_type); 99085a8e8a7SMatthew Dillon if (mrec->head.type == 99185a8e8a7SMatthew Dillon HAMMER_MREC_TYPE_REC_BADCRC) { 99285a8e8a7SMatthew Dillon printf(" (BAD CRC)"); 99385a8e8a7SMatthew Dillon } 99485a8e8a7SMatthew Dillon printf("\n"); 995a276dc6bSMatthew Dillon printf(" tids %016jx:%016jx data=%d\n", 996a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.create_tid, 997a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.delete_tid, 99817dd83bcSMatthew Dillon mrec->rec.leaf.data_len); 99917dd83bcSMatthew Dillon break; 100017dd83bcSMatthew Dillon case HAMMER_MREC_TYPE_PASS: 1001a276dc6bSMatthew Dillon printf("Pass obj=%016jx key=%016jx " 100217dd83bcSMatthew Dillon "rt=%02x ot=%02x\n", 1003a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.obj_id, 1004a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.key, 100517dd83bcSMatthew Dillon mrec->rec.leaf.base.rec_type, 100617dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_type); 1007a276dc6bSMatthew Dillon printf(" tids %016jx:%016jx data=%d\n", 1008a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.create_tid, 1009a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.delete_tid, 101017dd83bcSMatthew Dillon mrec->rec.leaf.data_len); 101117dd83bcSMatthew Dillon break; 101217dd83bcSMatthew Dillon case HAMMER_MREC_TYPE_SKIP: 1013a276dc6bSMatthew Dillon printf("Skip obj=%016jx key=%016jx rt=%02x to\n" 1014a276dc6bSMatthew Dillon " obj=%016jx key=%016jx rt=%02x\n", 1015a276dc6bSMatthew Dillon (uintmax_t)mrec->skip.skip_beg.obj_id, 1016a276dc6bSMatthew Dillon (uintmax_t)mrec->skip.skip_beg.key, 101717dd83bcSMatthew Dillon mrec->skip.skip_beg.rec_type, 1018a276dc6bSMatthew Dillon (uintmax_t)mrec->skip.skip_end.obj_id, 1019a276dc6bSMatthew Dillon (uintmax_t)mrec->skip.skip_end.key, 102017dd83bcSMatthew Dillon mrec->skip.skip_end.rec_type); 102117dd83bcSMatthew Dillon default: 102217dd83bcSMatthew Dillon break; 102317dd83bcSMatthew Dillon } 102417dd83bcSMatthew Dillon offset += bytes; 1025243ca327SMatthew Dillon } 1026243ca327SMatthew Dillon } 1027243ca327SMatthew Dillon 1028243ca327SMatthew Dillon /* 1029243ca327SMatthew Dillon * Read and process the termination sync record. 1030243ca327SMatthew Dillon */ 1031243ca327SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 103248eadef9SMatthew Dillon if (mrec == NULL || 103348eadef9SMatthew Dillon (mrec->head.type != HAMMER_MREC_TYPE_SYNC && 103448eadef9SMatthew Dillon mrec->head.type != HAMMER_MREC_TYPE_IDLE) 103548eadef9SMatthew Dillon ) { 1036243ca327SMatthew Dillon fprintf(stderr, "Mirror-dump: Did not get termination " 1037243ca327SMatthew Dillon "sync record\n"); 1038243ca327SMatthew Dillon } 103939e88285SMatthew Dillon 104039e88285SMatthew Dillon /* 104139e88285SMatthew Dillon * Continue with more batches until EOF. 104239e88285SMatthew Dillon */ 104339e88285SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 104439e88285SMatthew Dillon if (mrec) 104539e88285SMatthew Dillon goto again; 1046a7fbbf91SMatthew Dillon } 1047a7fbbf91SMatthew Dillon 1048a7fbbf91SMatthew Dillon void 104948eadef9SMatthew Dillon hammer_cmd_mirror_copy(char **av, int ac, int streaming) 1050a7fbbf91SMatthew Dillon { 105134ebae70SMatthew Dillon pid_t pid1; 105234ebae70SMatthew Dillon pid_t pid2; 105334ebae70SMatthew Dillon int fds[2]; 105408b71f9fSMatthew Dillon const char *xav[32]; 1055243ca327SMatthew Dillon char tbuf[16]; 105634ebae70SMatthew Dillon char *ptr; 1057243ca327SMatthew Dillon int xac; 105834ebae70SMatthew Dillon 105934ebae70SMatthew Dillon if (ac != 2) 106034ebae70SMatthew Dillon mirror_usage(1); 106134ebae70SMatthew Dillon 1062e7f926a5SMatthew Dillon TwoWayPipeOpt = 1; 10630bd7a37cSMatthew Dillon signal(SIGPIPE, SIG_IGN); 1064e7f926a5SMatthew Dillon 1065e7f926a5SMatthew Dillon again: 106634ebae70SMatthew Dillon if (pipe(fds) < 0) { 106734ebae70SMatthew Dillon perror("pipe"); 106834ebae70SMatthew Dillon exit(1); 106934ebae70SMatthew Dillon } 107034ebae70SMatthew Dillon 107134ebae70SMatthew Dillon /* 107234ebae70SMatthew Dillon * Source 107334ebae70SMatthew Dillon */ 107434ebae70SMatthew Dillon if ((pid1 = fork()) == 0) { 10750bd7a37cSMatthew Dillon signal(SIGPIPE, SIG_DFL); 107634ebae70SMatthew Dillon dup2(fds[0], 0); 107734ebae70SMatthew Dillon dup2(fds[0], 1); 107834ebae70SMatthew Dillon close(fds[0]); 107934ebae70SMatthew Dillon close(fds[1]); 108034ebae70SMatthew Dillon if ((ptr = strchr(av[0], ':')) != NULL) { 108134ebae70SMatthew Dillon *ptr++ = 0; 1082243ca327SMatthew Dillon xac = 0; 1083243ca327SMatthew Dillon xav[xac++] = "ssh"; 10843a998207SMatthew Dillon if (CompressOpt) 10853a998207SMatthew Dillon xav[xac++] = "-C"; 10866c45ca3eSMatthew Dillon if (SshPort) { 10876c45ca3eSMatthew Dillon xav[xac++] = "-p"; 10886c45ca3eSMatthew Dillon xav[xac++] = SshPort; 10896c45ca3eSMatthew Dillon } 1090243ca327SMatthew Dillon xav[xac++] = av[0]; 1091243ca327SMatthew Dillon xav[xac++] = "hammer"; 109248eadef9SMatthew Dillon 109348eadef9SMatthew Dillon switch(VerboseOpt) { 109448eadef9SMatthew Dillon case 0: 109548eadef9SMatthew Dillon break; 109648eadef9SMatthew Dillon case 1: 1097243ca327SMatthew Dillon xav[xac++] = "-v"; 109848eadef9SMatthew Dillon break; 109948eadef9SMatthew Dillon case 2: 110048eadef9SMatthew Dillon xav[xac++] = "-vv"; 110148eadef9SMatthew Dillon break; 110248eadef9SMatthew Dillon default: 110348eadef9SMatthew Dillon xav[xac++] = "-vvv"; 110448eadef9SMatthew Dillon break; 110548eadef9SMatthew Dillon } 110607485271SMichael Neumann if (ForceYesOpt) { 110707485271SMichael Neumann xav[xac++] = "-y"; 110807485271SMichael Neumann } 1109243ca327SMatthew Dillon xav[xac++] = "-2"; 1110243ca327SMatthew Dillon if (TimeoutOpt) { 1111243ca327SMatthew Dillon snprintf(tbuf, sizeof(tbuf), "%d", TimeoutOpt); 1112243ca327SMatthew Dillon xav[xac++] = "-t"; 1113243ca327SMatthew Dillon xav[xac++] = tbuf; 1114243ca327SMatthew Dillon } 111508b71f9fSMatthew Dillon if (SplitupOptStr) { 111608b71f9fSMatthew Dillon xav[xac++] = "-S"; 111708b71f9fSMatthew Dillon xav[xac++] = SplitupOptStr; 111808b71f9fSMatthew Dillon } 111948eadef9SMatthew Dillon if (streaming) 1120901f434aSMatthew Dillon xav[xac++] = "mirror-read-stream"; 112148eadef9SMatthew Dillon else 1122243ca327SMatthew Dillon xav[xac++] = "mirror-read"; 1123243ca327SMatthew Dillon xav[xac++] = ptr; 1124243ca327SMatthew Dillon xav[xac++] = NULL; 1125243ca327SMatthew Dillon execv("/usr/bin/ssh", (void *)xav); 112634ebae70SMatthew Dillon } else { 112748eadef9SMatthew Dillon hammer_cmd_mirror_read(av, 1, streaming); 1128243ca327SMatthew Dillon fflush(stdout); 1129243ca327SMatthew Dillon fflush(stderr); 113034ebae70SMatthew Dillon } 113153d93cc7SMatthew Dillon _exit(1); 113234ebae70SMatthew Dillon } 113334ebae70SMatthew Dillon 113434ebae70SMatthew Dillon /* 113534ebae70SMatthew Dillon * Target 113634ebae70SMatthew Dillon */ 113734ebae70SMatthew Dillon if ((pid2 = fork()) == 0) { 11380bd7a37cSMatthew Dillon signal(SIGPIPE, SIG_DFL); 113934ebae70SMatthew Dillon dup2(fds[1], 0); 114034ebae70SMatthew Dillon dup2(fds[1], 1); 114134ebae70SMatthew Dillon close(fds[0]); 114234ebae70SMatthew Dillon close(fds[1]); 114334ebae70SMatthew Dillon if ((ptr = strchr(av[1], ':')) != NULL) { 114434ebae70SMatthew Dillon *ptr++ = 0; 1145243ca327SMatthew Dillon xac = 0; 1146243ca327SMatthew Dillon xav[xac++] = "ssh"; 11473a998207SMatthew Dillon if (CompressOpt) 11483a998207SMatthew Dillon xav[xac++] = "-C"; 11496c45ca3eSMatthew Dillon if (SshPort) { 11506c45ca3eSMatthew Dillon xav[xac++] = "-p"; 11516c45ca3eSMatthew Dillon xav[xac++] = SshPort; 11526c45ca3eSMatthew Dillon } 1153243ca327SMatthew Dillon xav[xac++] = av[1]; 1154243ca327SMatthew Dillon xav[xac++] = "hammer"; 115548eadef9SMatthew Dillon 115648eadef9SMatthew Dillon switch(VerboseOpt) { 115748eadef9SMatthew Dillon case 0: 115848eadef9SMatthew Dillon break; 115948eadef9SMatthew Dillon case 1: 1160243ca327SMatthew Dillon xav[xac++] = "-v"; 116148eadef9SMatthew Dillon break; 116248eadef9SMatthew Dillon case 2: 116348eadef9SMatthew Dillon xav[xac++] = "-vv"; 116448eadef9SMatthew Dillon break; 116548eadef9SMatthew Dillon default: 116648eadef9SMatthew Dillon xav[xac++] = "-vvv"; 116748eadef9SMatthew Dillon break; 116848eadef9SMatthew Dillon } 116907485271SMichael Neumann if (ForceYesOpt) { 117007485271SMichael Neumann xav[xac++] = "-y"; 117107485271SMichael Neumann } 1172243ca327SMatthew Dillon xav[xac++] = "-2"; 1173243ca327SMatthew Dillon xav[xac++] = "mirror-write"; 1174243ca327SMatthew Dillon xav[xac++] = ptr; 1175243ca327SMatthew Dillon xav[xac++] = NULL; 1176243ca327SMatthew Dillon execv("/usr/bin/ssh", (void *)xav); 117734ebae70SMatthew Dillon } else { 117834ebae70SMatthew Dillon hammer_cmd_mirror_write(av + 1, 1); 1179243ca327SMatthew Dillon fflush(stdout); 1180243ca327SMatthew Dillon fflush(stderr); 118134ebae70SMatthew Dillon } 118253d93cc7SMatthew Dillon _exit(1); 118334ebae70SMatthew Dillon } 118434ebae70SMatthew Dillon close(fds[0]); 118534ebae70SMatthew Dillon close(fds[1]); 118634ebae70SMatthew Dillon 118734ebae70SMatthew Dillon while (waitpid(pid1, NULL, 0) <= 0) 118834ebae70SMatthew Dillon ; 118934ebae70SMatthew Dillon while (waitpid(pid2, NULL, 0) <= 0) 119034ebae70SMatthew Dillon ; 1191e7f926a5SMatthew Dillon 1192e7f926a5SMatthew Dillon /* 1193e7f926a5SMatthew Dillon * If the link is lost restart 1194e7f926a5SMatthew Dillon */ 1195e7f926a5SMatthew Dillon if (streaming) { 1196e7f926a5SMatthew Dillon if (VerboseOpt) { 1197e7f926a5SMatthew Dillon fprintf(stderr, "\nLost Link\n"); 1198e7f926a5SMatthew Dillon fflush(stderr); 1199e7f926a5SMatthew Dillon } 12000bd7a37cSMatthew Dillon sleep(15 + DelayOpt); 1201e7f926a5SMatthew Dillon goto again; 1202e7f926a5SMatthew Dillon } 1203e7f926a5SMatthew Dillon 1204a7fbbf91SMatthew Dillon } 1205a7fbbf91SMatthew Dillon 1206243ca327SMatthew Dillon /* 1207243ca327SMatthew Dillon * Read and return multiple mrecords 1208243ca327SMatthew Dillon */ 1209a7fbbf91SMatthew Dillon static int 121017dd83bcSMatthew Dillon read_mrecords(int fd, char *buf, u_int size, hammer_ioc_mrecord_head_t pickup) 1211a7fbbf91SMatthew Dillon { 121217dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 1213a7fbbf91SMatthew Dillon u_int count; 1214a7fbbf91SMatthew Dillon size_t n; 1215a7fbbf91SMatthew Dillon size_t i; 121617dd83bcSMatthew Dillon size_t bytes; 121785a8e8a7SMatthew Dillon int type; 1218a7fbbf91SMatthew Dillon 1219a7fbbf91SMatthew Dillon count = 0; 1220a7fbbf91SMatthew Dillon while (size - count >= HAMMER_MREC_HEADSIZE) { 1221a7fbbf91SMatthew Dillon /* 1222a7fbbf91SMatthew Dillon * Cached the record header in case we run out of buffer 1223a7fbbf91SMatthew Dillon * space. 1224a7fbbf91SMatthew Dillon */ 122517dd83bcSMatthew Dillon fflush(stdout); 1226a7fbbf91SMatthew Dillon if (pickup->signature == 0) { 1227a7fbbf91SMatthew Dillon for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) { 1228a7fbbf91SMatthew Dillon i = read(fd, (char *)pickup + n, 1229a7fbbf91SMatthew Dillon HAMMER_MREC_HEADSIZE - n); 1230a7fbbf91SMatthew Dillon if (i <= 0) 1231a7fbbf91SMatthew Dillon break; 1232a7fbbf91SMatthew Dillon } 1233a7fbbf91SMatthew Dillon if (n == 0) 1234a7fbbf91SMatthew Dillon break; 1235a7fbbf91SMatthew Dillon if (n != HAMMER_MREC_HEADSIZE) { 1236a7fbbf91SMatthew Dillon fprintf(stderr, "read_mrecords: short read on pipe\n"); 1237a7fbbf91SMatthew Dillon exit(1); 1238a7fbbf91SMatthew Dillon } 1239a7fbbf91SMatthew Dillon if (pickup->signature != HAMMER_IOC_MIRROR_SIGNATURE) { 124034bb69d8SThomas Nikolajsen fprintf(stderr, "read_mrecords: malformed record on pipe, " 124134bb69d8SThomas Nikolajsen "bad signature\n"); 1242a7fbbf91SMatthew Dillon exit(1); 1243a7fbbf91SMatthew Dillon } 1244a7fbbf91SMatthew Dillon } 1245a7fbbf91SMatthew Dillon if (pickup->rec_size < HAMMER_MREC_HEADSIZE || 124617dd83bcSMatthew Dillon pickup->rec_size > sizeof(*mrec) + HAMMER_XBUFSIZE) { 124734bb69d8SThomas Nikolajsen fprintf(stderr, "read_mrecords: malformed record on pipe, " 124834bb69d8SThomas Nikolajsen "illegal rec_size\n"); 1249a7fbbf91SMatthew Dillon exit(1); 1250a7fbbf91SMatthew Dillon } 1251a7fbbf91SMatthew Dillon 1252a7fbbf91SMatthew Dillon /* 1253a7fbbf91SMatthew Dillon * Stop if we have insufficient space for the record and data. 1254a7fbbf91SMatthew Dillon */ 125517dd83bcSMatthew Dillon bytes = HAMMER_HEAD_DOALIGN(pickup->rec_size); 125617dd83bcSMatthew Dillon if (size - count < bytes) 1257a7fbbf91SMatthew Dillon break; 1258a7fbbf91SMatthew Dillon 1259a7fbbf91SMatthew Dillon /* 126085a8e8a7SMatthew Dillon * Stop if the record type is not a REC, SKIP, or PASS, 126185a8e8a7SMatthew Dillon * which are the only types the ioctl supports. Other types 126285a8e8a7SMatthew Dillon * are used only by the userland protocol. 126385a8e8a7SMatthew Dillon * 126485a8e8a7SMatthew Dillon * Ignore all flags. 1265243ca327SMatthew Dillon */ 126685a8e8a7SMatthew Dillon type = pickup->type & HAMMER_MRECF_TYPE_LOMASK; 126785a8e8a7SMatthew Dillon if (type != HAMMER_MREC_TYPE_PFSD && 126885a8e8a7SMatthew Dillon type != HAMMER_MREC_TYPE_REC && 126985a8e8a7SMatthew Dillon type != HAMMER_MREC_TYPE_SKIP && 127085a8e8a7SMatthew Dillon type != HAMMER_MREC_TYPE_PASS) { 1271243ca327SMatthew Dillon break; 127217dd83bcSMatthew Dillon } 1273243ca327SMatthew Dillon 1274243ca327SMatthew Dillon /* 1275a7fbbf91SMatthew Dillon * Read the remainder and clear the pickup signature. 1276a7fbbf91SMatthew Dillon */ 127717dd83bcSMatthew Dillon for (n = HAMMER_MREC_HEADSIZE; n < bytes; n += i) { 127817dd83bcSMatthew Dillon i = read(fd, buf + count + n, bytes - n); 1279a7fbbf91SMatthew Dillon if (i <= 0) 1280a7fbbf91SMatthew Dillon break; 1281a7fbbf91SMatthew Dillon } 128217dd83bcSMatthew Dillon if (n != bytes) { 1283a7fbbf91SMatthew Dillon fprintf(stderr, "read_mrecords: short read on pipe\n"); 1284a7fbbf91SMatthew Dillon exit(1); 1285a7fbbf91SMatthew Dillon } 128617dd83bcSMatthew Dillon 128717dd83bcSMatthew Dillon bcopy(pickup, buf + count, HAMMER_MREC_HEADSIZE); 128817dd83bcSMatthew Dillon pickup->signature = 0; 128917dd83bcSMatthew Dillon pickup->type = 0; 129017dd83bcSMatthew Dillon mrec = (void *)(buf + count); 129117dd83bcSMatthew Dillon 129217dd83bcSMatthew Dillon /* 129317dd83bcSMatthew Dillon * Validate the completed record 129417dd83bcSMatthew Dillon */ 129517dd83bcSMatthew Dillon if (mrec->head.rec_crc != 129617dd83bcSMatthew Dillon crc32((char *)mrec + HAMMER_MREC_CRCOFF, 129717dd83bcSMatthew Dillon mrec->head.rec_size - HAMMER_MREC_CRCOFF)) { 129817dd83bcSMatthew Dillon fprintf(stderr, "read_mrecords: malformed record " 129917dd83bcSMatthew Dillon "on pipe, bad crc\n"); 130017dd83bcSMatthew Dillon exit(1); 1301a7fbbf91SMatthew Dillon } 1302a7fbbf91SMatthew Dillon 130317dd83bcSMatthew Dillon /* 130485a8e8a7SMatthew Dillon * If its a B-Tree record validate the data crc. 130585a8e8a7SMatthew Dillon * 130685a8e8a7SMatthew Dillon * NOTE: If the VFS passes us an explicitly errorde mrec 130785a8e8a7SMatthew Dillon * we just pass it through. 130817dd83bcSMatthew Dillon */ 130985a8e8a7SMatthew Dillon type = mrec->head.type & HAMMER_MRECF_TYPE_MASK; 131085a8e8a7SMatthew Dillon 131185a8e8a7SMatthew Dillon if (type == HAMMER_MREC_TYPE_REC) { 131217dd83bcSMatthew Dillon if (mrec->head.rec_size < 131317dd83bcSMatthew Dillon sizeof(mrec->rec) + mrec->rec.leaf.data_len) { 131417dd83bcSMatthew Dillon fprintf(stderr, 131517dd83bcSMatthew Dillon "read_mrecords: malformed record on " 131617dd83bcSMatthew Dillon "pipe, illegal element data_len\n"); 131717dd83bcSMatthew Dillon exit(1); 131817dd83bcSMatthew Dillon } 131917dd83bcSMatthew Dillon if (mrec->rec.leaf.data_len && 132017dd83bcSMatthew Dillon mrec->rec.leaf.data_offset && 132117dd83bcSMatthew Dillon hammer_crc_test_leaf(&mrec->rec + 1, &mrec->rec.leaf) == 0) { 132217dd83bcSMatthew Dillon fprintf(stderr, 132317dd83bcSMatthew Dillon "read_mrecords: data_crc did not " 1324a276dc6bSMatthew Dillon "match data! obj=%016jx key=%016jx\n", 1325a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.obj_id, 1326a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.key); 132717dd83bcSMatthew Dillon fprintf(stderr, 132817dd83bcSMatthew Dillon "continuing, but there are problems\n"); 132917dd83bcSMatthew Dillon } 133017dd83bcSMatthew Dillon } 133117dd83bcSMatthew Dillon count += bytes; 1332a7fbbf91SMatthew Dillon } 1333a7fbbf91SMatthew Dillon return(count); 1334a7fbbf91SMatthew Dillon } 1335a7fbbf91SMatthew Dillon 133634ebae70SMatthew Dillon /* 133717dd83bcSMatthew Dillon * Read and return a single mrecord. 1338243ca327SMatthew Dillon */ 1339243ca327SMatthew Dillon static 134017dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t 134117dd83bcSMatthew Dillon read_mrecord(int fdin, int *errorp, hammer_ioc_mrecord_head_t pickup) 1342243ca327SMatthew Dillon { 134317dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 134417dd83bcSMatthew Dillon struct hammer_ioc_mrecord_head mrechd; 1345243ca327SMatthew Dillon size_t bytes; 1346243ca327SMatthew Dillon size_t n; 1347243ca327SMatthew Dillon size_t i; 1348243ca327SMatthew Dillon 1349243ca327SMatthew Dillon if (pickup && pickup->type != 0) { 1350243ca327SMatthew Dillon mrechd = *pickup; 1351243ca327SMatthew Dillon pickup->signature = 0; 1352243ca327SMatthew Dillon pickup->type = 0; 1353243ca327SMatthew Dillon n = HAMMER_MREC_HEADSIZE; 1354243ca327SMatthew Dillon } else { 1355243ca327SMatthew Dillon /* 1356243ca327SMatthew Dillon * Read in the PFSD header from the sender. 1357243ca327SMatthew Dillon */ 1358243ca327SMatthew Dillon for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) { 1359243ca327SMatthew Dillon i = read(fdin, (char *)&mrechd + n, HAMMER_MREC_HEADSIZE - n); 1360243ca327SMatthew Dillon if (i <= 0) 1361243ca327SMatthew Dillon break; 1362243ca327SMatthew Dillon } 1363243ca327SMatthew Dillon if (n == 0) { 1364243ca327SMatthew Dillon *errorp = 0; /* EOF */ 1365243ca327SMatthew Dillon return(NULL); 1366243ca327SMatthew Dillon } 1367243ca327SMatthew Dillon if (n != HAMMER_MREC_HEADSIZE) { 1368243ca327SMatthew Dillon fprintf(stderr, "short read of mrecord header\n"); 1369243ca327SMatthew Dillon *errorp = EPIPE; 1370243ca327SMatthew Dillon return(NULL); 1371243ca327SMatthew Dillon } 1372243ca327SMatthew Dillon } 1373243ca327SMatthew Dillon if (mrechd.signature != HAMMER_IOC_MIRROR_SIGNATURE) { 1374243ca327SMatthew Dillon fprintf(stderr, "read_mrecord: bad signature\n"); 1375243ca327SMatthew Dillon *errorp = EINVAL; 1376243ca327SMatthew Dillon return(NULL); 1377243ca327SMatthew Dillon } 137817dd83bcSMatthew Dillon bytes = HAMMER_HEAD_DOALIGN(mrechd.rec_size); 137917dd83bcSMatthew Dillon assert(bytes >= sizeof(mrechd)); 1380243ca327SMatthew Dillon mrec = malloc(bytes); 138117dd83bcSMatthew Dillon mrec->head = mrechd; 138217dd83bcSMatthew Dillon 1383243ca327SMatthew Dillon while (n < bytes) { 1384243ca327SMatthew Dillon i = read(fdin, (char *)mrec + n, bytes - n); 1385243ca327SMatthew Dillon if (i <= 0) 1386243ca327SMatthew Dillon break; 1387243ca327SMatthew Dillon n += i; 1388243ca327SMatthew Dillon } 1389243ca327SMatthew Dillon if (n != bytes) { 1390243ca327SMatthew Dillon fprintf(stderr, "read_mrecord: short read on payload\n"); 1391243ca327SMatthew Dillon *errorp = EPIPE; 1392243ca327SMatthew Dillon return(NULL); 1393243ca327SMatthew Dillon } 139417dd83bcSMatthew Dillon if (mrec->head.rec_crc != 139517dd83bcSMatthew Dillon crc32((char *)mrec + HAMMER_MREC_CRCOFF, 139617dd83bcSMatthew Dillon mrec->head.rec_size - HAMMER_MREC_CRCOFF)) { 1397243ca327SMatthew Dillon fprintf(stderr, "read_mrecord: bad CRC\n"); 1398243ca327SMatthew Dillon *errorp = EINVAL; 1399243ca327SMatthew Dillon return(NULL); 1400243ca327SMatthew Dillon } 1401243ca327SMatthew Dillon *errorp = 0; 1402243ca327SMatthew Dillon return(mrec); 1403243ca327SMatthew Dillon } 1404243ca327SMatthew Dillon 1405243ca327SMatthew Dillon static 1406243ca327SMatthew Dillon void 140717dd83bcSMatthew Dillon write_mrecord(int fdout, u_int32_t type, hammer_ioc_mrecord_any_t mrec, 140817dd83bcSMatthew Dillon int bytes) 1409243ca327SMatthew Dillon { 141017dd83bcSMatthew Dillon char zbuf[HAMMER_HEAD_ALIGN]; 141117dd83bcSMatthew Dillon int pad; 1412243ca327SMatthew Dillon 141317dd83bcSMatthew Dillon pad = HAMMER_HEAD_DOALIGN(bytes) - bytes; 141417dd83bcSMatthew Dillon 141517dd83bcSMatthew Dillon assert(bytes >= (int)sizeof(mrec->head)); 141617dd83bcSMatthew Dillon bzero(&mrec->head, sizeof(mrec->head)); 141717dd83bcSMatthew Dillon mrec->head.signature = HAMMER_IOC_MIRROR_SIGNATURE; 141817dd83bcSMatthew Dillon mrec->head.type = type; 141917dd83bcSMatthew Dillon mrec->head.rec_size = bytes; 142017dd83bcSMatthew Dillon mrec->head.rec_crc = crc32((char *)mrec + HAMMER_MREC_CRCOFF, 142117dd83bcSMatthew Dillon bytes - HAMMER_MREC_CRCOFF); 142217dd83bcSMatthew Dillon if (write(fdout, mrec, bytes) != bytes) { 1423243ca327SMatthew Dillon fprintf(stderr, "write_mrecord: error %d (%s)\n", 1424243ca327SMatthew Dillon errno, strerror(errno)); 1425243ca327SMatthew Dillon exit(1); 1426243ca327SMatthew Dillon } 142717dd83bcSMatthew Dillon if (pad) { 142817dd83bcSMatthew Dillon bzero(zbuf, pad); 142917dd83bcSMatthew Dillon if (write(fdout, zbuf, pad) != pad) { 143017dd83bcSMatthew Dillon fprintf(stderr, "write_mrecord: error %d (%s)\n", 143117dd83bcSMatthew Dillon errno, strerror(errno)); 143217dd83bcSMatthew Dillon exit(1); 143317dd83bcSMatthew Dillon } 143417dd83bcSMatthew Dillon } 1435243ca327SMatthew Dillon } 1436243ca327SMatthew Dillon 1437243ca327SMatthew Dillon /* 143834ebae70SMatthew Dillon * Generate a mirroring header with the pfs information of the 143934ebae70SMatthew Dillon * originating filesytem. 144034ebae70SMatthew Dillon */ 144134ebae70SMatthew Dillon static void 1442e7f926a5SMatthew Dillon generate_mrec_header(int fd, int pfs_id, 1443e7f926a5SMatthew Dillon union hammer_ioc_mrecord_any *mrec_tmp) 144434ebae70SMatthew Dillon { 144534ebae70SMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 144634ebae70SMatthew Dillon 144734ebae70SMatthew Dillon bzero(&pfs, sizeof(pfs)); 1448e7f926a5SMatthew Dillon bzero(mrec_tmp, sizeof(*mrec_tmp)); 1449d4e5b69bSMatthew Dillon pfs.pfs_id = pfs_id; 1450e7f926a5SMatthew Dillon pfs.ondisk = &mrec_tmp->pfs.pfsd; 1451e7f926a5SMatthew Dillon pfs.bytes = sizeof(mrec_tmp->pfs.pfsd); 145234ebae70SMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 14539dc76cb1SMatthew Dillon fprintf(stderr, "Mirror-read: not a HAMMER fs/pseudofs!\n"); 145434ebae70SMatthew Dillon exit(1); 145534ebae70SMatthew Dillon } 145634ebae70SMatthew Dillon if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) { 14579dc76cb1SMatthew Dillon fprintf(stderr, "Mirror-read: HAMMER pfs version mismatch!\n"); 145834ebae70SMatthew Dillon exit(1); 145934ebae70SMatthew Dillon } 1460e7f926a5SMatthew Dillon mrec_tmp->pfs.version = pfs.version; 146134ebae70SMatthew Dillon } 146234ebae70SMatthew Dillon 146334ebae70SMatthew Dillon /* 146434ebae70SMatthew Dillon * Validate the pfs information from the originating filesystem 146534ebae70SMatthew Dillon * against the target filesystem. shared_uuid must match. 146648eadef9SMatthew Dillon * 146748eadef9SMatthew Dillon * return -1 if we got a TERM record 146834ebae70SMatthew Dillon */ 146948eadef9SMatthew Dillon static int 1470d4e5b69bSMatthew Dillon validate_mrec_header(int fd, int fdin, int is_target, int pfs_id, 147148eadef9SMatthew Dillon struct hammer_ioc_mrecord_head *pickup, 147234ebae70SMatthew Dillon hammer_tid_t *tid_begp, hammer_tid_t *tid_endp) 147334ebae70SMatthew Dillon { 147434ebae70SMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 147534ebae70SMatthew Dillon struct hammer_pseudofs_data pfsd; 147617dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 1477243ca327SMatthew Dillon int error; 147834ebae70SMatthew Dillon 147934ebae70SMatthew Dillon /* 148034ebae70SMatthew Dillon * Get the PFSD info from the target filesystem. 148134ebae70SMatthew Dillon */ 148234ebae70SMatthew Dillon bzero(&pfs, sizeof(pfs)); 148334ebae70SMatthew Dillon bzero(&pfsd, sizeof(pfsd)); 1484d4e5b69bSMatthew Dillon pfs.pfs_id = pfs_id; 148534ebae70SMatthew Dillon pfs.ondisk = &pfsd; 148634ebae70SMatthew Dillon pfs.bytes = sizeof(pfsd); 148734ebae70SMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 148834ebae70SMatthew Dillon fprintf(stderr, "mirror-write: not a HAMMER fs/pseudofs!\n"); 148934ebae70SMatthew Dillon exit(1); 149034ebae70SMatthew Dillon } 149134ebae70SMatthew Dillon if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) { 149234ebae70SMatthew Dillon fprintf(stderr, "mirror-write: HAMMER pfs version mismatch!\n"); 149334ebae70SMatthew Dillon exit(1); 149434ebae70SMatthew Dillon } 149534ebae70SMatthew Dillon 149648eadef9SMatthew Dillon mrec = read_mrecord(fdin, &error, pickup); 1497243ca327SMatthew Dillon if (mrec == NULL) { 1498243ca327SMatthew Dillon if (error == 0) 1499243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: short read\n"); 150034ebae70SMatthew Dillon exit(1); 150134ebae70SMatthew Dillon } 150248eadef9SMatthew Dillon if (mrec->head.type == HAMMER_MREC_TYPE_TERM) { 150348eadef9SMatthew Dillon free(mrec); 150448eadef9SMatthew Dillon return(-1); 150548eadef9SMatthew Dillon } 150648eadef9SMatthew Dillon 150717dd83bcSMatthew Dillon if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) { 1508243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: did not get expected " 1509243ca327SMatthew Dillon "PFSD record type\n"); 151034ebae70SMatthew Dillon exit(1); 151134ebae70SMatthew Dillon } 151217dd83bcSMatthew Dillon if (mrec->head.rec_size != sizeof(mrec->pfs)) { 1513243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: unexpected payload " 1514243ca327SMatthew Dillon "size\n"); 151534ebae70SMatthew Dillon exit(1); 151634ebae70SMatthew Dillon } 151717dd83bcSMatthew Dillon if (mrec->pfs.version != pfs.version) { 1518243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: Version mismatch\n"); 151934ebae70SMatthew Dillon exit(1); 152034ebae70SMatthew Dillon } 152134ebae70SMatthew Dillon 152234ebae70SMatthew Dillon /* 152334ebae70SMatthew Dillon * Whew. Ok, is the read PFS info compatible with the target? 152434ebae70SMatthew Dillon */ 152517dd83bcSMatthew Dillon if (bcmp(&mrec->pfs.pfsd.shared_uuid, &pfsd.shared_uuid, 152617dd83bcSMatthew Dillon sizeof(pfsd.shared_uuid)) != 0) { 152717dd83bcSMatthew Dillon fprintf(stderr, 152817dd83bcSMatthew Dillon "mirror-write: source and target have " 1529f265b84fSSascha Wildner "different shared-uuid's!\n"); 153034ebae70SMatthew Dillon exit(1); 153134ebae70SMatthew Dillon } 1532d4e5b69bSMatthew Dillon if (is_target && 1533d4e5b69bSMatthew Dillon (pfsd.mirror_flags & HAMMER_PFSD_SLAVE) == 0) { 153434ebae70SMatthew Dillon fprintf(stderr, "mirror-write: target must be in slave mode\n"); 153534ebae70SMatthew Dillon exit(1); 153634ebae70SMatthew Dillon } 1537d4e5b69bSMatthew Dillon if (tid_begp) 153817dd83bcSMatthew Dillon *tid_begp = mrec->pfs.pfsd.sync_beg_tid; 1539d4e5b69bSMatthew Dillon if (tid_endp) 154017dd83bcSMatthew Dillon *tid_endp = mrec->pfs.pfsd.sync_end_tid; 1541243ca327SMatthew Dillon free(mrec); 154248eadef9SMatthew Dillon return(0); 154334ebae70SMatthew Dillon } 154434ebae70SMatthew Dillon 154534ebae70SMatthew Dillon static void 1546d4e5b69bSMatthew Dillon update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id) 154734ebae70SMatthew Dillon { 1548243ca327SMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 1549243ca327SMatthew Dillon struct hammer_pseudofs_data pfsd; 155034ebae70SMatthew Dillon 1551243ca327SMatthew Dillon bzero(&pfs, sizeof(pfs)); 1552243ca327SMatthew Dillon bzero(&pfsd, sizeof(pfsd)); 1553d4e5b69bSMatthew Dillon pfs.pfs_id = pfs_id; 1554243ca327SMatthew Dillon pfs.ondisk = &pfsd; 1555243ca327SMatthew Dillon pfs.bytes = sizeof(pfsd); 1556243ca327SMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 1557243ca327SMatthew Dillon perror("update_pfs_snapshot (read)"); 1558243ca327SMatthew Dillon exit(1); 155934ebae70SMatthew Dillon } 15609c67b4d2SMatthew Dillon if (pfsd.sync_end_tid != snapshot_tid) { 1561ddc8e722SMatthew Dillon pfsd.sync_end_tid = snapshot_tid; 1562243ca327SMatthew Dillon if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) != 0) { 1563243ca327SMatthew Dillon perror("update_pfs_snapshot (rewrite)"); 1564243ca327SMatthew Dillon exit(1); 156534ebae70SMatthew Dillon } 156648eadef9SMatthew Dillon if (VerboseOpt >= 2) { 15679dc76cb1SMatthew Dillon fprintf(stderr, 1568ced4470dSMatthew Dillon "Mirror-write: Completed, updated snapshot " 1569a276dc6bSMatthew Dillon "to %016jx\n", 1570a276dc6bSMatthew Dillon (uintmax_t)snapshot_tid); 1571ced4470dSMatthew Dillon fflush(stderr); 1572243ca327SMatthew Dillon } 15739c67b4d2SMatthew Dillon } 157448eadef9SMatthew Dillon } 1575243ca327SMatthew Dillon 157648eadef9SMatthew Dillon /* 157748eadef9SMatthew Dillon * Bandwidth-limited write in chunks 157848eadef9SMatthew Dillon */ 157948eadef9SMatthew Dillon static 158048eadef9SMatthew Dillon ssize_t 158148eadef9SMatthew Dillon writebw(int fd, const void *buf, size_t nbytes, 158248eadef9SMatthew Dillon u_int64_t *bwcount, struct timeval *tv1) 158348eadef9SMatthew Dillon { 158448eadef9SMatthew Dillon struct timeval tv2; 158548eadef9SMatthew Dillon size_t n; 158648eadef9SMatthew Dillon ssize_t r; 158748eadef9SMatthew Dillon ssize_t a; 158848eadef9SMatthew Dillon int usec; 158948eadef9SMatthew Dillon 159048eadef9SMatthew Dillon a = 0; 159148eadef9SMatthew Dillon r = 0; 159248eadef9SMatthew Dillon while (nbytes) { 159348eadef9SMatthew Dillon if (*bwcount + nbytes > BandwidthOpt) 159448eadef9SMatthew Dillon n = BandwidthOpt - *bwcount; 159548eadef9SMatthew Dillon else 159648eadef9SMatthew Dillon n = nbytes; 159748eadef9SMatthew Dillon if (n) 159848eadef9SMatthew Dillon r = write(fd, buf, n); 159948eadef9SMatthew Dillon if (r >= 0) { 160048eadef9SMatthew Dillon a += r; 160148eadef9SMatthew Dillon nbytes -= r; 160248eadef9SMatthew Dillon buf = (const char *)buf + r; 160348eadef9SMatthew Dillon } 160448eadef9SMatthew Dillon if ((size_t)r != n) 160548eadef9SMatthew Dillon break; 160648eadef9SMatthew Dillon *bwcount += n; 160748eadef9SMatthew Dillon if (*bwcount >= BandwidthOpt) { 160848eadef9SMatthew Dillon gettimeofday(&tv2, NULL); 160948eadef9SMatthew Dillon usec = (int)(tv2.tv_sec - tv1->tv_sec) * 1000000 + 161048eadef9SMatthew Dillon (int)(tv2.tv_usec - tv1->tv_usec); 161148eadef9SMatthew Dillon if (usec >= 0 && usec < 1000000) 161248eadef9SMatthew Dillon usleep(1000000 - usec); 161348eadef9SMatthew Dillon gettimeofday(tv1, NULL); 161448eadef9SMatthew Dillon *bwcount -= BandwidthOpt; 161548eadef9SMatthew Dillon } 161648eadef9SMatthew Dillon } 161748eadef9SMatthew Dillon return(a ? a : r); 161848eadef9SMatthew Dillon } 161934ebae70SMatthew Dillon 162001a72c9fSMatthew Dillon /* 162101a72c9fSMatthew Dillon * Get a yes or no answer from the terminal. The program may be run as 162201a72c9fSMatthew Dillon * part of a two-way pipe so we cannot use stdin for this operation. 162301a72c9fSMatthew Dillon */ 162401a72c9fSMatthew Dillon static int 162501a72c9fSMatthew Dillon getyn(void) 162601a72c9fSMatthew Dillon { 162701a72c9fSMatthew Dillon char buf[256]; 162801a72c9fSMatthew Dillon FILE *fp; 162901a72c9fSMatthew Dillon int result; 163001a72c9fSMatthew Dillon 163101a72c9fSMatthew Dillon fp = fopen("/dev/tty", "r"); 163201a72c9fSMatthew Dillon if (fp == NULL) { 163301a72c9fSMatthew Dillon fprintf(stderr, "No terminal for response\n"); 163401a72c9fSMatthew Dillon return(-1); 163501a72c9fSMatthew Dillon } 163601a72c9fSMatthew Dillon result = -1; 163701a72c9fSMatthew Dillon while (fgets(buf, sizeof(buf), fp) != NULL) { 163801a72c9fSMatthew Dillon if (buf[0] == 'y' || buf[0] == 'Y') { 163901a72c9fSMatthew Dillon result = 1; 164001a72c9fSMatthew Dillon break; 164101a72c9fSMatthew Dillon } 164201a72c9fSMatthew Dillon if (buf[0] == 'n' || buf[0] == 'N') { 164301a72c9fSMatthew Dillon result = 0; 164401a72c9fSMatthew Dillon break; 164501a72c9fSMatthew Dillon } 164601a72c9fSMatthew Dillon fprintf(stderr, "Response not understood\n"); 164701a72c9fSMatthew Dillon break; 164801a72c9fSMatthew Dillon } 164901a72c9fSMatthew Dillon fclose(fp); 165001a72c9fSMatthew Dillon return(result); 165101a72c9fSMatthew Dillon } 165201a72c9fSMatthew Dillon 1653a7fbbf91SMatthew Dillon static void 1654a7fbbf91SMatthew Dillon mirror_usage(int code) 1655a7fbbf91SMatthew Dillon { 1656a7fbbf91SMatthew Dillon fprintf(stderr, 165734bb69d8SThomas Nikolajsen "hammer mirror-read <filesystem> [begin-tid]\n" 165834bb69d8SThomas Nikolajsen "hammer mirror-read-stream <filesystem> [begin-tid]\n" 1659a7fbbf91SMatthew Dillon "hammer mirror-write <filesystem>\n" 1660243ca327SMatthew Dillon "hammer mirror-dump\n" 166134bb69d8SThomas Nikolajsen "hammer mirror-copy [[user@]host:]<filesystem>" 166234bb69d8SThomas Nikolajsen " [[user@]host:]<filesystem>\n" 166334bb69d8SThomas Nikolajsen "hammer mirror-stream [[user@]host:]<filesystem>" 166434bb69d8SThomas Nikolajsen " [[user@]host:]<filesystem>\n" 1665a7fbbf91SMatthew Dillon ); 1666a7fbbf91SMatthew Dillon exit(code); 1667a7fbbf91SMatthew Dillon } 166801a72c9fSMatthew Dillon 1669