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; 908b640662SAntonio Huete Jimenez 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]; 11169f5a58cSMatthew 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); 160*764d0c8cSTomohiro Kusumi free(buf); 161*764d0c8cSTomohiro Kusumi free(histogram_ary); 16248eadef9SMatthew Dillon return; 16348eadef9SMatthew Dillon } 16448eadef9SMatthew Dillon ++mirror.tid_beg; 16539e88285SMatthew Dillon } else if (streaming && histogram) { 16639e88285SMatthew Dillon mirror.tid_beg = histogram_ary[histindex].tid + 1; 16739e88285SMatthew Dillon } else { 16839e88285SMatthew Dillon mirror.tid_beg = 0; 16948eadef9SMatthew Dillon } 170d4e5b69bSMatthew Dillon 171d4e5b69bSMatthew Dillon /* 172d4e5b69bSMatthew Dillon * Write out the PFS header, tid_beg will be updated if our PFS 173d4e5b69bSMatthew Dillon * has a larger begin sync. tid_end is set to the latest source 174d4e5b69bSMatthew Dillon * TID whos flush cycle has completed. 175d4e5b69bSMatthew Dillon */ 176e7f926a5SMatthew Dillon generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp); 177e7f926a5SMatthew Dillon if (mirror.tid_beg < mrec_tmp.pfs.pfsd.sync_beg_tid) 178e7f926a5SMatthew Dillon mirror.tid_beg = mrec_tmp.pfs.pfsd.sync_beg_tid; 179e7f926a5SMatthew Dillon mirror.tid_end = mrec_tmp.pfs.pfsd.sync_end_tid; 180e7f926a5SMatthew Dillon mirror.ubuf = buf; 181e7f926a5SMatthew Dillon mirror.size = SERIALBUF_SIZE; 182e7f926a5SMatthew Dillon mirror.pfs_id = pfs.pfs_id; 183e7f926a5SMatthew Dillon mirror.shared_uuid = pfs.ondisk->shared_uuid; 184d4e5b69bSMatthew Dillon 185d4e5b69bSMatthew Dillon /* 186e7f926a5SMatthew Dillon * XXX If the histogram is exhausted and the TID delta is large 187e7f926a5SMatthew Dillon * the stream might have been offline for a while and is 188e7f926a5SMatthew Dillon * now picking it up again. Do another histogram. 189d4e5b69bSMatthew Dillon */ 190e7f926a5SMatthew Dillon #if 0 19139e88285SMatthew Dillon if (streaming && histogram && histindex == histend) { 192e7f926a5SMatthew Dillon if (mirror.tid_end - mirror.tid_beg > BULK_MINIMUM) 193ced4470dSMatthew Dillon histogram = 0; 194e7f926a5SMatthew Dillon } 195e7f926a5SMatthew Dillon #endif 19634ebae70SMatthew Dillon 197e7f926a5SMatthew Dillon /* 198e7f926a5SMatthew Dillon * Initial bulk startup control, try to do some incremental 199e7f926a5SMatthew Dillon * mirroring in order to allow the stream to be killed and 200e7f926a5SMatthew Dillon * restarted without having to start over. 201e7f926a5SMatthew Dillon */ 202ced4470dSMatthew Dillon if (histogram == 0 && BulkOpt == 0) { 20339e88285SMatthew Dillon if (VerboseOpt && repeat == 0) { 204e7f926a5SMatthew Dillon fprintf(stderr, "\n"); 20539e88285SMatthew Dillon sameline = 0; 20639e88285SMatthew Dillon } 207ced4470dSMatthew Dillon histmax = generate_histogram(fd, filesystem, 20839e88285SMatthew Dillon &histogram_ary, &mirror, 20939e88285SMatthew Dillon &repeat); 210ced4470dSMatthew Dillon histindex = 0; 211ced4470dSMatthew Dillon histogram = 1; 21239e88285SMatthew Dillon 21339e88285SMatthew Dillon /* 21439e88285SMatthew Dillon * Just stream the histogram, then stop 21539e88285SMatthew Dillon */ 21639e88285SMatthew Dillon if (streaming == 0) 21739e88285SMatthew Dillon streaming = -1; 218e7f926a5SMatthew Dillon } 219e7f926a5SMatthew Dillon 22039e88285SMatthew Dillon if (streaming && histogram) { 221ced4470dSMatthew Dillon ++histindex; 222ced4470dSMatthew Dillon mirror.tid_end = histogram_ary[histindex].tid; 223ced4470dSMatthew Dillon estbytes = histogram_ary[histindex-1].bytes; 224e7f926a5SMatthew Dillon mrec_tmp.pfs.pfsd.sync_end_tid = mirror.tid_end; 225ced4470dSMatthew Dillon } else { 226ced4470dSMatthew Dillon estbytes = 0; 227e7f926a5SMatthew Dillon } 228e7f926a5SMatthew Dillon 229e7f926a5SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_PFSD, 230e7f926a5SMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.pfs)); 231e7f926a5SMatthew Dillon 232e7f926a5SMatthew Dillon /* 233e7f926a5SMatthew Dillon * A cycle file overrides the beginning TID only if we are 23439e88285SMatthew Dillon * not operating in two-way or histogram mode. 235e7f926a5SMatthew Dillon */ 23639e88285SMatthew Dillon if (TwoWayPipeOpt == 0 && histogram == 0) { 237e7f926a5SMatthew Dillon hammer_get_cycle(&mirror.key_beg, &mirror.tid_beg); 238e7f926a5SMatthew Dillon } 239e7f926a5SMatthew Dillon 240e7f926a5SMatthew Dillon /* 241e7f926a5SMatthew Dillon * An additional argument overrides the beginning TID regardless 242e7f926a5SMatthew Dillon * of what mode we are in. This is not recommending if operating 243e7f926a5SMatthew Dillon * in two-way mode. 244e7f926a5SMatthew Dillon */ 24517dd83bcSMatthew Dillon if (ac == 2) 24617dd83bcSMatthew Dillon mirror.tid_beg = strtoull(av[1], NULL, 0); 24717dd83bcSMatthew Dillon 24848eadef9SMatthew Dillon if (streaming == 0 || VerboseOpt >= 2) { 24948eadef9SMatthew Dillon fprintf(stderr, 250ced4470dSMatthew Dillon "Mirror-read: Mirror %016jx to %016jx", 251a276dc6bSMatthew Dillon (uintmax_t)mirror.tid_beg, (uintmax_t)mirror.tid_end); 252ced4470dSMatthew Dillon if (histogram) 253ced4470dSMatthew Dillon fprintf(stderr, " (bulk= %ju)", (uintmax_t)estbytes); 2543d7b2393SMatthew Dillon fprintf(stderr, "\n"); 2553d7b2393SMatthew Dillon fflush(stderr); 25648eadef9SMatthew Dillon } 257243ca327SMatthew Dillon if (mirror.key_beg.obj_id != (int64_t)HAMMER_MIN_OBJID) { 258a276dc6bSMatthew Dillon fprintf(stderr, "Mirror-read: Resuming at object %016jx\n", 259a276dc6bSMatthew Dillon (uintmax_t)mirror.key_beg.obj_id); 260243ca327SMatthew Dillon } 261243ca327SMatthew Dillon 262243ca327SMatthew Dillon /* 2639dc76cb1SMatthew Dillon * Nothing to do if begin equals end. 2649dc76cb1SMatthew Dillon */ 26548eadef9SMatthew Dillon if (mirror.tid_beg >= mirror.tid_end) { 26648eadef9SMatthew Dillon if (streaming == 0 || VerboseOpt >= 2) 26748eadef9SMatthew Dillon fprintf(stderr, "Mirror-read: No work to do\n"); 26839e88285SMatthew Dillon sleep(DelayOpt); 26948eadef9SMatthew Dillon didwork = 0; 27039e88285SMatthew Dillon histogram = 0; 2719dc76cb1SMatthew Dillon goto done; 2729dc76cb1SMatthew Dillon } 27348eadef9SMatthew Dillon didwork = 1; 2749dc76cb1SMatthew Dillon 2759dc76cb1SMatthew Dillon /* 276243ca327SMatthew Dillon * Write out bulk records 277243ca327SMatthew Dillon */ 278a7fbbf91SMatthew Dillon mirror.ubuf = buf; 279a7fbbf91SMatthew Dillon mirror.size = SERIALBUF_SIZE; 280a7fbbf91SMatthew Dillon 281a7fbbf91SMatthew Dillon do { 282a7fbbf91SMatthew Dillon mirror.count = 0; 283d4e5b69bSMatthew Dillon mirror.pfs_id = pfs.pfs_id; 284d4e5b69bSMatthew Dillon mirror.shared_uuid = pfs.ondisk->shared_uuid; 285a7fbbf91SMatthew Dillon if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) { 286269cdd19SMatthew Dillon score_printf(LINE3, "Mirror-read %s failed: %s", 287269cdd19SMatthew Dillon filesystem, strerror(errno)); 288a7fbbf91SMatthew Dillon fprintf(stderr, "Mirror-read %s failed: %s\n", 289a7fbbf91SMatthew Dillon filesystem, strerror(errno)); 290a7fbbf91SMatthew Dillon exit(1); 291a7fbbf91SMatthew Dillon } 2929c67b4d2SMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) { 293269cdd19SMatthew Dillon score_printf(LINE3, "Mirror-read %s fatal error %d", 294269cdd19SMatthew Dillon filesystem, mirror.head.error); 2959c67b4d2SMatthew Dillon fprintf(stderr, 2969c67b4d2SMatthew Dillon "Mirror-read %s fatal error %d\n", 2979c67b4d2SMatthew Dillon filesystem, mirror.head.error); 2989c67b4d2SMatthew Dillon exit(1); 2999c67b4d2SMatthew Dillon } 300243ca327SMatthew Dillon if (mirror.count) { 30148eadef9SMatthew Dillon if (BandwidthOpt) { 30248eadef9SMatthew Dillon n = writebw(1, mirror.ubuf, mirror.count, 30348eadef9SMatthew Dillon &bwcount, &bwtv); 30448eadef9SMatthew Dillon } else { 305243ca327SMatthew Dillon n = write(1, mirror.ubuf, mirror.count); 30648eadef9SMatthew Dillon } 307243ca327SMatthew Dillon if (n != mirror.count) { 308269cdd19SMatthew Dillon score_printf(LINE3, 309269cdd19SMatthew Dillon "Mirror-read %s failed: " 310269cdd19SMatthew Dillon "short write", 311269cdd19SMatthew Dillon filesystem); 312269cdd19SMatthew Dillon fprintf(stderr, 313269cdd19SMatthew Dillon "Mirror-read %s failed: " 314243ca327SMatthew Dillon "short write\n", 315243ca327SMatthew Dillon filesystem); 316243ca327SMatthew Dillon exit(1); 317243ca327SMatthew Dillon } 318a7fbbf91SMatthew Dillon } 31948eadef9SMatthew Dillon total_bytes += mirror.count; 32048eadef9SMatthew Dillon if (streaming && VerboseOpt) { 321e7f926a5SMatthew Dillon fprintf(stderr, 32239e88285SMatthew Dillon "\rscan obj=%016jx tids=%016jx:%016jx %11jd", 323a276dc6bSMatthew Dillon (uintmax_t)mirror.key_cur.obj_id, 324a276dc6bSMatthew Dillon (uintmax_t)mirror.tid_beg, 325a276dc6bSMatthew Dillon (uintmax_t)mirror.tid_end, 326a276dc6bSMatthew Dillon (intmax_t)total_bytes); 32748eadef9SMatthew Dillon fflush(stderr); 32839e88285SMatthew Dillon sameline = 0; 329269cdd19SMatthew Dillon } else if (streaming) { 330269cdd19SMatthew Dillon score_printf(LINE2, 331269cdd19SMatthew Dillon "obj=%016jx tids=%016jx:%016jx %11jd", 332269cdd19SMatthew Dillon (uintmax_t)mirror.key_cur.obj_id, 333269cdd19SMatthew Dillon (uintmax_t)mirror.tid_beg, 334269cdd19SMatthew Dillon (uintmax_t)mirror.tid_end, 335269cdd19SMatthew Dillon (intmax_t)total_bytes); 33648eadef9SMatthew Dillon } 337a7fbbf91SMatthew Dillon mirror.key_beg = mirror.key_cur; 338e7f926a5SMatthew Dillon 339e7f926a5SMatthew Dillon /* 340e7f926a5SMatthew Dillon * Deal with time limit option 341e7f926a5SMatthew Dillon */ 342243ca327SMatthew Dillon if (TimeoutOpt && 343243ca327SMatthew Dillon (unsigned)(time(NULL) - base_t) > (unsigned)TimeoutOpt) { 344269cdd19SMatthew Dillon score_printf(LINE3, 345269cdd19SMatthew Dillon "Mirror-read %s interrupted by timer at" 346269cdd19SMatthew Dillon " %016jx", 347269cdd19SMatthew Dillon filesystem, 348269cdd19SMatthew Dillon (uintmax_t)mirror.key_cur.obj_id); 349243ca327SMatthew Dillon fprintf(stderr, 350243ca327SMatthew Dillon "Mirror-read %s interrupted by timer at" 351a276dc6bSMatthew Dillon " %016jx\n", 352243ca327SMatthew Dillon filesystem, 353a276dc6bSMatthew Dillon (uintmax_t)mirror.key_cur.obj_id); 354243ca327SMatthew Dillon interrupted = 1; 355243ca327SMatthew Dillon break; 356243ca327SMatthew Dillon } 357a7fbbf91SMatthew Dillon } while (mirror.count != 0); 358a7fbbf91SMatthew Dillon 35948eadef9SMatthew Dillon done: 36039e88285SMatthew Dillon if (streaming && VerboseOpt && sameline == 0) { 361ced4470dSMatthew Dillon fprintf(stderr, "\n"); 362ced4470dSMatthew Dillon fflush(stderr); 36339e88285SMatthew Dillon sameline = 1; 364ced4470dSMatthew Dillon } 365ced4470dSMatthew Dillon 366243ca327SMatthew Dillon /* 36748eadef9SMatthew Dillon * Write out the termination sync record - only if not interrupted 368243ca327SMatthew Dillon */ 36948eadef9SMatthew Dillon if (interrupted == 0) { 37048eadef9SMatthew Dillon if (didwork) { 37117dd83bcSMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_SYNC, 37217dd83bcSMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.sync)); 37348eadef9SMatthew Dillon } else { 37448eadef9SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_IDLE, 37548eadef9SMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.sync)); 37648eadef9SMatthew Dillon } 37748eadef9SMatthew Dillon } 37834ebae70SMatthew Dillon 379243ca327SMatthew Dillon /* 380243ca327SMatthew Dillon * If the -2 option was given (automatic when doing mirror-copy), 381243ca327SMatthew Dillon * a two-way pipe is assumed and we expect a response mrec from 382243ca327SMatthew Dillon * the target. 383243ca327SMatthew Dillon */ 384243ca327SMatthew Dillon if (TwoWayPipeOpt) { 38548eadef9SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 38617dd83bcSMatthew Dillon if (mrec == NULL || 38717dd83bcSMatthew Dillon mrec->head.type != HAMMER_MREC_TYPE_UPDATE || 38817dd83bcSMatthew Dillon mrec->head.rec_size != sizeof(mrec->update)) { 389243ca327SMatthew Dillon fprintf(stderr, "mirror_read: Did not get final " 390243ca327SMatthew Dillon "acknowledgement packet from target\n"); 391243ca327SMatthew Dillon exit(1); 392243ca327SMatthew Dillon } 393243ca327SMatthew Dillon if (interrupted) { 394243ca327SMatthew Dillon if (CyclePath) { 395ced4470dSMatthew Dillon hammer_set_cycle(&mirror.key_cur, 396ced4470dSMatthew Dillon mirror.tid_beg); 39734bb69d8SThomas Nikolajsen fprintf(stderr, "Cyclefile %s updated for " 39834bb69d8SThomas Nikolajsen "continuation\n", CyclePath); 399243ca327SMatthew Dillon } 400243ca327SMatthew Dillon } else { 40117dd83bcSMatthew Dillon sync_tid = mrec->update.tid; 402243ca327SMatthew Dillon if (CyclePath) { 403243ca327SMatthew Dillon hammer_key_beg_init(&mirror.key_beg); 404243ca327SMatthew Dillon hammer_set_cycle(&mirror.key_beg, sync_tid); 405a276dc6bSMatthew Dillon fprintf(stderr, 406a276dc6bSMatthew Dillon "Cyclefile %s updated to 0x%016jx\n", 407a276dc6bSMatthew Dillon CyclePath, (uintmax_t)sync_tid); 408243ca327SMatthew Dillon } 409243ca327SMatthew Dillon } 410cc71ff00STomohiro Kusumi free(mrec); 411243ca327SMatthew Dillon } else if (CyclePath) { 412243ca327SMatthew Dillon /* NOTE! mirror.tid_beg cannot be updated */ 413243ca327SMatthew Dillon fprintf(stderr, "Warning: cycle file (-c option) cannot be " 414243ca327SMatthew Dillon "fully updated unless you use mirror-copy\n"); 415243ca327SMatthew Dillon hammer_set_cycle(&mirror.key_beg, mirror.tid_beg); 416243ca327SMatthew Dillon } 41748eadef9SMatthew Dillon if (streaming && interrupted == 0) { 41848eadef9SMatthew Dillon time_t t1 = time(NULL); 41948eadef9SMatthew Dillon time_t t2; 42048eadef9SMatthew Dillon 421e7f926a5SMatthew Dillon /* 42239e88285SMatthew Dillon * Try to break down large bulk transfers into smaller ones 42339e88285SMatthew Dillon * so it can sync the transaction id on the slave. This 42439e88285SMatthew Dillon * way if we get interrupted a restart doesn't have to 42539e88285SMatthew Dillon * start from scratch. 426e7f926a5SMatthew Dillon */ 42739e88285SMatthew Dillon if (streaming && histogram) { 428ced4470dSMatthew Dillon if (histindex != histmax) { 42939e88285SMatthew Dillon if (VerboseOpt && VerboseOpt < 2 && 43039e88285SMatthew Dillon streaming >= 0) { 431e7f926a5SMatthew Dillon fprintf(stderr, " (bulk incremental)"); 43239e88285SMatthew Dillon } 43339e88285SMatthew Dillon relpfs(fd, &pfs); 434e7f926a5SMatthew Dillon goto again; 435e7f926a5SMatthew Dillon } 436ced4470dSMatthew Dillon } 437e7f926a5SMatthew Dillon 43839e88285SMatthew Dillon if (VerboseOpt && streaming >= 0) { 43948eadef9SMatthew Dillon fprintf(stderr, " W"); 44048eadef9SMatthew Dillon fflush(stderr); 441269cdd19SMatthew Dillon } else if (streaming >= 0) { 442269cdd19SMatthew Dillon score_printf(LINE1, "Waiting"); 44348eadef9SMatthew Dillon } 44448eadef9SMatthew Dillon pfs.ondisk->sync_end_tid = mirror.tid_end; 44539e88285SMatthew Dillon if (streaming < 0) { 44639e88285SMatthew Dillon /* 44739e88285SMatthew Dillon * Fake streaming mode when using a histogram to 44839e88285SMatthew Dillon * break up a mirror-read, do not wait on source. 44939e88285SMatthew Dillon */ 45039e88285SMatthew Dillon streaming = 0; 45139e88285SMatthew Dillon } else if (ioctl(fd, HAMMERIOC_WAI_PSEUDOFS, &pfs) < 0) { 452269cdd19SMatthew Dillon score_printf(LINE3, 453269cdd19SMatthew Dillon "Mirror-read %s: cannot stream: %s\n", 454269cdd19SMatthew Dillon filesystem, strerror(errno)); 455269cdd19SMatthew Dillon fprintf(stderr, 456269cdd19SMatthew Dillon "Mirror-read %s: cannot stream: %s\n", 45748eadef9SMatthew Dillon filesystem, strerror(errno)); 45848eadef9SMatthew Dillon } else { 45948eadef9SMatthew Dillon t2 = time(NULL) - t1; 46048eadef9SMatthew Dillon if (t2 >= 0 && t2 < DelayOpt) { 46148eadef9SMatthew Dillon if (VerboseOpt) { 46248eadef9SMatthew Dillon fprintf(stderr, "\bD"); 46348eadef9SMatthew Dillon fflush(stderr); 46448eadef9SMatthew Dillon } 46548eadef9SMatthew Dillon sleep(DelayOpt - t2); 46648eadef9SMatthew Dillon } 46748eadef9SMatthew Dillon if (VerboseOpt) { 46848eadef9SMatthew Dillon fprintf(stderr, "\b "); 46948eadef9SMatthew Dillon fflush(stderr); 47048eadef9SMatthew Dillon } 47148eadef9SMatthew Dillon relpfs(fd, &pfs); 47248eadef9SMatthew Dillon goto again; 47348eadef9SMatthew Dillon } 47448eadef9SMatthew Dillon } 47548eadef9SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_TERM, 47648eadef9SMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.sync)); 47748eadef9SMatthew Dillon relpfs(fd, &pfs); 478*764d0c8cSTomohiro Kusumi free(buf); 479*764d0c8cSTomohiro Kusumi free(histogram_ary); 480a7fbbf91SMatthew Dillon fprintf(stderr, "Mirror-read %s succeeded\n", filesystem); 481a7fbbf91SMatthew Dillon } 482a7fbbf91SMatthew Dillon 483e7f926a5SMatthew Dillon /* 4843d7b2393SMatthew Dillon * What we are trying to do here is figure out how much data is 4853d7b2393SMatthew Dillon * going to be sent for the TID range and to break the TID range 4863d7b2393SMatthew Dillon * down into reasonably-sized slices (from the point of view of 4873d7b2393SMatthew Dillon * data sent) so a lost connection can restart at a reasonable 4883d7b2393SMatthew Dillon * place and not all the way back at the beginning. 489ced4470dSMatthew Dillon * 490ced4470dSMatthew Dillon * An entry's TID serves as the end_tid for the prior entry 491ced4470dSMatthew Dillon * So we have to offset the calculation by 1 so that TID falls into 492ced4470dSMatthew Dillon * the previous entry when populating entries. 493ced4470dSMatthew Dillon * 494ced4470dSMatthew Dillon * Because the transaction id space is bursty we need a relatively 495ced4470dSMatthew Dillon * large number of buckets (like a million) to do a reasonable job 496ced4470dSMatthew Dillon * for things like an initial bulk mirrors on a very large filesystem. 497e7f926a5SMatthew Dillon */ 498ced4470dSMatthew Dillon #define HIST_COUNT (1024 * 1024) 4993d7b2393SMatthew Dillon 500e7f926a5SMatthew Dillon static int 501e7f926a5SMatthew Dillon generate_histogram(int fd, const char *filesystem, 502ced4470dSMatthew Dillon histogram_t *histogram_ary, 50339e88285SMatthew Dillon struct hammer_ioc_mirror_rw *mirror_base, 50439e88285SMatthew Dillon int *repeatp) 505e7f926a5SMatthew Dillon { 506e7f926a5SMatthew Dillon struct hammer_ioc_mirror_rw mirror; 5073d7b2393SMatthew Dillon union hammer_ioc_mrecord_any *mrec; 508e7f926a5SMatthew Dillon hammer_tid_t tid_beg; 509e7f926a5SMatthew Dillon hammer_tid_t tid_end; 510ced4470dSMatthew Dillon hammer_tid_t tid; 511ced4470dSMatthew Dillon hammer_tid_t tidx; 512ced4470dSMatthew Dillon u_int64_t *tid_bytes; 5133d7b2393SMatthew Dillon u_int64_t total; 5143d7b2393SMatthew Dillon u_int64_t accum; 515269cdd19SMatthew Dillon int chunkno; 516e7f926a5SMatthew Dillon int i; 5173d7b2393SMatthew Dillon int res; 5183d7b2393SMatthew Dillon int off; 5193d7b2393SMatthew Dillon int len; 520e7f926a5SMatthew Dillon 521e7f926a5SMatthew Dillon mirror = *mirror_base; 522e7f926a5SMatthew Dillon tid_beg = mirror.tid_beg; 523e7f926a5SMatthew Dillon tid_end = mirror.tid_end; 5243d7b2393SMatthew Dillon mirror.head.flags |= HAMMER_IOC_MIRROR_NODATA; 525e7f926a5SMatthew Dillon 5263d7b2393SMatthew Dillon if (*histogram_ary == NULL) { 527ced4470dSMatthew Dillon *histogram_ary = malloc(sizeof(struct histogram) * 5283d7b2393SMatthew Dillon (HIST_COUNT + 2)); 5293d7b2393SMatthew Dillon } 5303d7b2393SMatthew Dillon if (tid_beg >= tid_end) 531e7f926a5SMatthew Dillon return(0); 532e7f926a5SMatthew Dillon 533ced4470dSMatthew Dillon /* needs 2 extra */ 534ced4470dSMatthew Dillon tid_bytes = malloc(sizeof(*tid_bytes) * (HIST_COUNT + 2)); 5354839c61eSSascha Wildner bzero(tid_bytes, sizeof(*tid_bytes) * (HIST_COUNT + 2)); 536ced4470dSMatthew Dillon 53739e88285SMatthew Dillon if (*repeatp == 0) { 5383d7b2393SMatthew Dillon fprintf(stderr, "Prescan to break up bulk transfer"); 5393d7b2393SMatthew Dillon if (VerboseOpt > 1) 5403d7b2393SMatthew Dillon fprintf(stderr, " (%juMB chunks)", 5413d7b2393SMatthew Dillon (uintmax_t)(SplitupOpt / (1024 * 1024))); 5423d7b2393SMatthew Dillon fprintf(stderr, "\n"); 54339e88285SMatthew Dillon } 544e7f926a5SMatthew Dillon 545ced4470dSMatthew Dillon /* 546ced4470dSMatthew Dillon * Note: (tid_beg,tid_end), range is inclusive of both beg & end. 547ced4470dSMatthew Dillon * 548ced4470dSMatthew Dillon * Note: Estimates can be off when the mirror is way behind due 549ced4470dSMatthew Dillon * to skips. 550ced4470dSMatthew Dillon */ 5513d7b2393SMatthew Dillon total = 0; 5523d7b2393SMatthew Dillon accum = 0; 553269cdd19SMatthew Dillon chunkno = 0; 5543d7b2393SMatthew Dillon for (;;) { 5553d7b2393SMatthew Dillon mirror.count = 0; 556e7f926a5SMatthew Dillon if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) { 557e7f926a5SMatthew Dillon fprintf(stderr, "Mirror-read %s failed: %s\n", 558e7f926a5SMatthew Dillon filesystem, strerror(errno)); 559e7f926a5SMatthew Dillon exit(1); 560e7f926a5SMatthew Dillon } 561e7f926a5SMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) { 562e7f926a5SMatthew Dillon fprintf(stderr, 563e7f926a5SMatthew Dillon "Mirror-read %s fatal error %d\n", 564e7f926a5SMatthew Dillon filesystem, mirror.head.error); 565e7f926a5SMatthew Dillon exit(1); 566e7f926a5SMatthew Dillon } 5673d7b2393SMatthew Dillon for (off = 0; 5683d7b2393SMatthew Dillon off < mirror.count; 5693d7b2393SMatthew Dillon off += HAMMER_HEAD_DOALIGN(mrec->head.rec_size) 5703d7b2393SMatthew Dillon ) { 5713d7b2393SMatthew Dillon mrec = (void *)((char *)mirror.ubuf + off); 572e7f926a5SMatthew Dillon 573e7f926a5SMatthew Dillon /* 574ced4470dSMatthew Dillon * We only care about general RECs and PASS 575ced4470dSMatthew Dillon * records. We ignore SKIPs. 576e7f926a5SMatthew Dillon */ 577ced4470dSMatthew Dillon switch (mrec->head.type & HAMMER_MRECF_TYPE_LOMASK) { 578ced4470dSMatthew Dillon case HAMMER_MREC_TYPE_REC: 579ced4470dSMatthew Dillon case HAMMER_MREC_TYPE_PASS: 580ced4470dSMatthew Dillon break; 581ced4470dSMatthew Dillon default: 582ced4470dSMatthew Dillon continue; 583e7f926a5SMatthew Dillon } 5843d7b2393SMatthew Dillon 585ced4470dSMatthew Dillon /* 586ced4470dSMatthew Dillon * Calculate for two indices, create_tid and 587ced4470dSMatthew Dillon * delete_tid. Record data only applies to 588ced4470dSMatthew Dillon * the create_tid. 589ced4470dSMatthew Dillon * 590ced4470dSMatthew Dillon * When tid is exactly on the boundary it really 591ced4470dSMatthew Dillon * belongs to the previous entry because scans 592ced4470dSMatthew Dillon * are inclusive of the ending entry. 593ced4470dSMatthew Dillon */ 594ced4470dSMatthew Dillon tid = mrec->rec.leaf.base.delete_tid; 595ced4470dSMatthew Dillon if (tid && tid >= tid_beg && tid <= tid_end) { 596ced4470dSMatthew Dillon len = HAMMER_HEAD_DOALIGN(mrec->head.rec_size); 597ced4470dSMatthew Dillon if (mrec->head.type == 598ced4470dSMatthew Dillon HAMMER_MREC_TYPE_REC) { 599ced4470dSMatthew Dillon len -= HAMMER_HEAD_DOALIGN( 600ced4470dSMatthew Dillon mrec->rec.leaf.data_len); 601ced4470dSMatthew Dillon assert(len > 0); 602ced4470dSMatthew Dillon } 603ced4470dSMatthew Dillon i = (tid - tid_beg) * HIST_COUNT / 6043d7b2393SMatthew Dillon (tid_end - tid_beg); 605ced4470dSMatthew Dillon tidx = tid_beg + i * (tid_end - tid_beg) / 606ced4470dSMatthew Dillon HIST_COUNT; 607ced4470dSMatthew Dillon if (tid == tidx && i) 608ced4470dSMatthew Dillon --i; 609ced4470dSMatthew Dillon assert(i >= 0 && i < HIST_COUNT); 6103d7b2393SMatthew Dillon tid_bytes[i] += len; 6113d7b2393SMatthew Dillon total += len; 6123d7b2393SMatthew Dillon accum += len; 6133d7b2393SMatthew Dillon } 614ced4470dSMatthew Dillon 615ced4470dSMatthew Dillon tid = mrec->rec.leaf.base.create_tid; 616ced4470dSMatthew Dillon if (tid && tid >= tid_beg && tid <= tid_end) { 617ced4470dSMatthew Dillon len = HAMMER_HEAD_DOALIGN(mrec->head.rec_size); 618ced4470dSMatthew Dillon if (mrec->head.type == 619ced4470dSMatthew Dillon HAMMER_MREC_TYPE_REC_NODATA) { 620ced4470dSMatthew Dillon len += HAMMER_HEAD_DOALIGN( 621ced4470dSMatthew Dillon mrec->rec.leaf.data_len); 622ced4470dSMatthew Dillon } 623ced4470dSMatthew Dillon i = (tid - tid_beg) * HIST_COUNT / 624ced4470dSMatthew Dillon (tid_end - tid_beg); 625ced4470dSMatthew Dillon tidx = tid_beg + i * (tid_end - tid_beg) / 626ced4470dSMatthew Dillon HIST_COUNT; 627ced4470dSMatthew Dillon if (tid == tidx && i) 628ced4470dSMatthew Dillon --i; 629ced4470dSMatthew Dillon assert(i >= 0 && i < HIST_COUNT); 630ced4470dSMatthew Dillon tid_bytes[i] += len; 631ced4470dSMatthew Dillon total += len; 632ced4470dSMatthew Dillon accum += len; 6333d7b2393SMatthew Dillon } 6343d7b2393SMatthew Dillon } 63539e88285SMatthew Dillon if (*repeatp == 0 && accum > SplitupOpt) { 636269cdd19SMatthew Dillon if (VerboseOpt > 1) { 6373d7b2393SMatthew Dillon fprintf(stderr, "."); 6383d7b2393SMatthew Dillon fflush(stderr); 6393d7b2393SMatthew Dillon } 640269cdd19SMatthew Dillon ++chunkno; 641269cdd19SMatthew Dillon score_printf(LINE2, "Prescan chunk %d", chunkno); 642269cdd19SMatthew Dillon accum = 0; 6433d7b2393SMatthew Dillon } 6443d7b2393SMatthew Dillon if (mirror.count == 0) 6453d7b2393SMatthew Dillon break; 6463d7b2393SMatthew Dillon mirror.key_beg = mirror.key_cur; 6473d7b2393SMatthew Dillon } 6483d7b2393SMatthew Dillon 6493d7b2393SMatthew Dillon /* 650d6c40a21SFrançois Tigeot * Reduce to SplitupOpt (default 4GB) chunks. This code may 651ced4470dSMatthew Dillon * use up to two additional elements. Do the array in-place. 652ced4470dSMatthew Dillon * 653ced4470dSMatthew Dillon * Inefficient degenerate cases can occur if we do not accumulate 654ced4470dSMatthew Dillon * at least the requested split amount, so error on the side of 655ced4470dSMatthew Dillon * going over a bit. 6563d7b2393SMatthew Dillon */ 6573d7b2393SMatthew Dillon res = 0; 658ced4470dSMatthew Dillon (*histogram_ary)[res].tid = tid_beg; 659ced4470dSMatthew Dillon (*histogram_ary)[res].bytes = tid_bytes[0]; 660ced4470dSMatthew Dillon for (i = 1; i < HIST_COUNT; ++i) { 661ced4470dSMatthew Dillon if ((*histogram_ary)[res].bytes >= SplitupOpt) { 662ced4470dSMatthew Dillon ++res; 663ced4470dSMatthew Dillon (*histogram_ary)[res].tid = tid_beg + 6643d7b2393SMatthew Dillon i * (tid_end - tid_beg) / 6653d7b2393SMatthew Dillon HIST_COUNT; 666ced4470dSMatthew Dillon (*histogram_ary)[res].bytes = 0; 667ced4470dSMatthew Dillon 6683d7b2393SMatthew Dillon } 669ced4470dSMatthew Dillon (*histogram_ary)[res].bytes += tid_bytes[i]; 6703d7b2393SMatthew Dillon } 671ced4470dSMatthew Dillon ++res; 672ced4470dSMatthew Dillon (*histogram_ary)[res].tid = tid_end; 673ced4470dSMatthew Dillon (*histogram_ary)[res].bytes = -1; 674ced4470dSMatthew Dillon 67539e88285SMatthew Dillon if (*repeatp == 0) { 6763d7b2393SMatthew Dillon if (VerboseOpt > 1) 6773d7b2393SMatthew Dillon fprintf(stderr, "\n"); /* newline after ... */ 678269cdd19SMatthew Dillon score_printf(LINE3, "Prescan %d chunks, total %ju MBytes", 679269cdd19SMatthew Dillon res, (uintmax_t)total / (1024 * 1024)); 68027eff55eSMatthew Dillon fprintf(stderr, "Prescan %d chunks, total %ju MBytes (", 6813d7b2393SMatthew Dillon res, (uintmax_t)total / (1024 * 1024)); 682ced4470dSMatthew Dillon for (i = 0; i < res && i < 3; ++i) { 683ced4470dSMatthew Dillon if (i) 684ced4470dSMatthew Dillon fprintf(stderr, ", "); 68539e88285SMatthew Dillon fprintf(stderr, "%ju", 68639e88285SMatthew Dillon (uintmax_t)(*histogram_ary)[i].bytes); 687ced4470dSMatthew Dillon } 688ced4470dSMatthew Dillon if (i < res) 689ced4470dSMatthew Dillon fprintf(stderr, ", ..."); 690ced4470dSMatthew Dillon fprintf(stderr, ")\n"); 69139e88285SMatthew Dillon } 69239e88285SMatthew Dillon assert(res <= HIST_COUNT); 69339e88285SMatthew Dillon *repeatp = 1; 694ced4470dSMatthew Dillon 695ced4470dSMatthew Dillon free(tid_bytes); 6963d7b2393SMatthew Dillon return(res); 697e7f926a5SMatthew Dillon } 698e7f926a5SMatthew Dillon 69901a72c9fSMatthew Dillon static void 70001a72c9fSMatthew Dillon create_pfs(const char *filesystem, uuid_t *s_uuid) 70101a72c9fSMatthew Dillon { 702f414d101SSascha Wildner if (ForceYesOpt == 1) { 70307485271SMichael Neumann fprintf(stderr, "PFS slave %s does not exist. " 70407485271SMichael Neumann "Auto create new slave PFS!\n", filesystem); 70507485271SMichael Neumann 706f414d101SSascha Wildner } else { 70701a72c9fSMatthew Dillon fprintf(stderr, "PFS slave %s does not exist.\n" 70801a72c9fSMatthew Dillon "Do you want to create a new slave PFS? (yes|no) ", 70901a72c9fSMatthew Dillon filesystem); 71001a72c9fSMatthew Dillon fflush(stderr); 71101a72c9fSMatthew Dillon if (getyn() != 1) { 71201a72c9fSMatthew Dillon fprintf(stderr, "Aborting operation\n"); 71301a72c9fSMatthew Dillon exit(1); 71401a72c9fSMatthew Dillon } 71507485271SMichael Neumann } 71601a72c9fSMatthew Dillon 71701a72c9fSMatthew Dillon u_int32_t status; 71801a72c9fSMatthew Dillon char *shared_uuid = NULL; 71901a72c9fSMatthew Dillon uuid_to_string(s_uuid, &shared_uuid, &status); 72001a72c9fSMatthew Dillon 72101a72c9fSMatthew Dillon char *cmd = NULL; 72201a72c9fSMatthew Dillon asprintf(&cmd, "/sbin/hammer pfs-slave '%s' shared-uuid=%s 1>&2", 72301a72c9fSMatthew Dillon filesystem, shared_uuid); 72401a72c9fSMatthew Dillon free(shared_uuid); 72501a72c9fSMatthew Dillon 72601a72c9fSMatthew Dillon if (cmd == NULL) { 72701a72c9fSMatthew Dillon fprintf(stderr, "Failed to alloc memory\n"); 72801a72c9fSMatthew Dillon exit(1); 72901a72c9fSMatthew Dillon } 73001a72c9fSMatthew Dillon if (system(cmd) != 0) { 73101a72c9fSMatthew Dillon fprintf(stderr, "Failed to create PFS\n"); 73201a72c9fSMatthew Dillon } 73301a72c9fSMatthew Dillon free(cmd); 73401a72c9fSMatthew Dillon } 73501a72c9fSMatthew Dillon 73617dd83bcSMatthew Dillon /* 73717dd83bcSMatthew Dillon * Pipe the mirroring data stream on stdin to the HAMMER VFS, adding 73817dd83bcSMatthew Dillon * some additional packet types to negotiate TID ranges and to verify 73917dd83bcSMatthew Dillon * completion. The HAMMER VFS does most of the work. 74017dd83bcSMatthew Dillon * 74117dd83bcSMatthew Dillon * It is important to note that the mirror.key_{beg,end} range must 74217dd83bcSMatthew Dillon * match the ranged used by the original. For now both sides use 74317dd83bcSMatthew Dillon * range the entire key space. 74417dd83bcSMatthew Dillon * 74517dd83bcSMatthew Dillon * It is even more important that the records in the stream conform 74617dd83bcSMatthew Dillon * to the TID range also supplied in the stream. The HAMMER VFS will 74717dd83bcSMatthew Dillon * use the REC, PASS, and SKIP record types to track the portions of 74817dd83bcSMatthew Dillon * the B-Tree being scanned in order to be able to proactively delete 74917dd83bcSMatthew Dillon * records on the target within those active areas that are not mentioned 75017dd83bcSMatthew Dillon * by the source. 75117dd83bcSMatthew Dillon * 75217dd83bcSMatthew Dillon * The mirror.key_cur field is used by the VFS to do this tracking. It 75317dd83bcSMatthew Dillon * must be initialized to key_beg but then is persistently updated by 75417dd83bcSMatthew Dillon * the HAMMER VFS on each successive ioctl() call. If you blow up this 75517dd83bcSMatthew Dillon * field you will blow up the mirror target, possibly to the point of 75617dd83bcSMatthew Dillon * deleting everything. As a safety measure the HAMMER VFS simply marks 75717dd83bcSMatthew Dillon * the records that the source has destroyed as deleted on the target, 75817dd83bcSMatthew Dillon * and normal pruning operations will deal with their final disposition 75917dd83bcSMatthew Dillon * at some later time. 76017dd83bcSMatthew Dillon */ 761a7fbbf91SMatthew Dillon void 762a7fbbf91SMatthew Dillon hammer_cmd_mirror_write(char **av, int ac) 763a7fbbf91SMatthew Dillon { 764a7fbbf91SMatthew Dillon struct hammer_ioc_mirror_rw mirror; 7658b640662SAntonio Huete Jimenez char *filesystem; 766a7fbbf91SMatthew Dillon char *buf = malloc(SERIALBUF_SIZE); 767d4e5b69bSMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 76817dd83bcSMatthew Dillon struct hammer_ioc_mrecord_head pickup; 769243ca327SMatthew Dillon struct hammer_ioc_synctid synctid; 77017dd83bcSMatthew Dillon union hammer_ioc_mrecord_any mrec_tmp; 77117dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 77201a72c9fSMatthew Dillon struct stat st; 773243ca327SMatthew Dillon int error; 774243ca327SMatthew Dillon int fd; 77548eadef9SMatthew Dillon int n; 776a7fbbf91SMatthew Dillon 77734bb69d8SThomas Nikolajsen if (ac != 1) 778a7fbbf91SMatthew Dillon mirror_usage(1); 779a7fbbf91SMatthew Dillon filesystem = av[0]; 78069f5a58cSMatthew Dillon hammer_check_restrict(filesystem); 781a7fbbf91SMatthew Dillon 78248eadef9SMatthew Dillon pickup.signature = 0; 78348eadef9SMatthew Dillon pickup.type = 0; 78448eadef9SMatthew Dillon 78548eadef9SMatthew Dillon again: 786a7fbbf91SMatthew Dillon bzero(&mirror, sizeof(mirror)); 787a7fbbf91SMatthew Dillon hammer_key_beg_init(&mirror.key_beg); 788a7fbbf91SMatthew Dillon hammer_key_end_init(&mirror.key_end); 78917dd83bcSMatthew Dillon mirror.key_end = mirror.key_beg; 790a7fbbf91SMatthew Dillon 79101a72c9fSMatthew Dillon /* 79201a72c9fSMatthew Dillon * Read initial packet 79301a72c9fSMatthew Dillon */ 79401a72c9fSMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 79501a72c9fSMatthew Dillon if (mrec == NULL) { 79601a72c9fSMatthew Dillon if (error == 0) 79701a72c9fSMatthew Dillon fprintf(stderr, "validate_mrec_header: short read\n"); 79801a72c9fSMatthew Dillon exit(1); 79901a72c9fSMatthew Dillon } 80001a72c9fSMatthew Dillon /* 80101a72c9fSMatthew Dillon * Validate packet 80201a72c9fSMatthew Dillon */ 80301a72c9fSMatthew Dillon if (mrec->head.type == HAMMER_MREC_TYPE_TERM) { 804*764d0c8cSTomohiro Kusumi free(buf); 80501a72c9fSMatthew Dillon return; 80601a72c9fSMatthew Dillon } 80701a72c9fSMatthew Dillon if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) { 80801a72c9fSMatthew Dillon fprintf(stderr, "validate_mrec_header: did not get expected " 80901a72c9fSMatthew Dillon "PFSD record type\n"); 81001a72c9fSMatthew Dillon exit(1); 81101a72c9fSMatthew Dillon } 81201a72c9fSMatthew Dillon if (mrec->head.rec_size != sizeof(mrec->pfs)) { 81301a72c9fSMatthew Dillon fprintf(stderr, "validate_mrec_header: unexpected payload " 81401a72c9fSMatthew Dillon "size\n"); 81501a72c9fSMatthew Dillon exit(1); 81601a72c9fSMatthew Dillon } 81701a72c9fSMatthew Dillon 81801a72c9fSMatthew Dillon /* 81901a72c9fSMatthew Dillon * Create slave PFS if it doesn't yet exist 82001a72c9fSMatthew Dillon */ 82101a72c9fSMatthew Dillon if (lstat(filesystem, &st) != 0) { 82201a72c9fSMatthew Dillon create_pfs(filesystem, &mrec->pfs.pfsd.shared_uuid); 82301a72c9fSMatthew Dillon } 82401a72c9fSMatthew Dillon free(mrec); 82501a72c9fSMatthew Dillon mrec = NULL; 82601a72c9fSMatthew Dillon 827d4e5b69bSMatthew Dillon fd = getpfs(&pfs, filesystem); 828a7fbbf91SMatthew Dillon 829243ca327SMatthew Dillon /* 830d4e5b69bSMatthew Dillon * In two-way mode the target writes out a PFS packet first. 831d4e5b69bSMatthew Dillon * The source uses our tid_end as its tid_beg by default, 832d4e5b69bSMatthew Dillon * picking up where it left off. 833243ca327SMatthew Dillon */ 834d4e5b69bSMatthew Dillon mirror.tid_beg = 0; 835d4e5b69bSMatthew Dillon if (TwoWayPipeOpt) { 836e7f926a5SMatthew Dillon generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp); 837e7f926a5SMatthew Dillon if (mirror.tid_beg < mrec_tmp.pfs.pfsd.sync_beg_tid) 838e7f926a5SMatthew Dillon mirror.tid_beg = mrec_tmp.pfs.pfsd.sync_beg_tid; 839e7f926a5SMatthew Dillon mirror.tid_end = mrec_tmp.pfs.pfsd.sync_end_tid; 840e7f926a5SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_PFSD, 841e7f926a5SMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.pfs)); 842d4e5b69bSMatthew Dillon } 843d4e5b69bSMatthew Dillon 844d4e5b69bSMatthew Dillon /* 84517dd83bcSMatthew Dillon * Read and process the PFS header. The source informs us of 84617dd83bcSMatthew Dillon * the TID range the stream represents. 847d4e5b69bSMatthew Dillon */ 84848eadef9SMatthew Dillon n = validate_mrec_header(fd, 0, 1, pfs.pfs_id, &pickup, 849d4e5b69bSMatthew Dillon &mirror.tid_beg, &mirror.tid_end); 85048eadef9SMatthew Dillon if (n < 0) { /* got TERM record */ 85148eadef9SMatthew Dillon relpfs(fd, &pfs); 852*764d0c8cSTomohiro Kusumi free(buf); 85348eadef9SMatthew Dillon return; 85448eadef9SMatthew Dillon } 85534ebae70SMatthew Dillon 856a7fbbf91SMatthew Dillon mirror.ubuf = buf; 857a7fbbf91SMatthew Dillon mirror.size = SERIALBUF_SIZE; 858a7fbbf91SMatthew Dillon 859243ca327SMatthew Dillon /* 86017dd83bcSMatthew Dillon * Read and process bulk records (REC, PASS, and SKIP types). 86117dd83bcSMatthew Dillon * 86217dd83bcSMatthew Dillon * On your life, do NOT mess with mirror.key_cur or your mirror 86317dd83bcSMatthew Dillon * target may become history. 864243ca327SMatthew Dillon */ 865a7fbbf91SMatthew Dillon for (;;) { 866a7fbbf91SMatthew Dillon mirror.count = 0; 867d4e5b69bSMatthew Dillon mirror.pfs_id = pfs.pfs_id; 868d4e5b69bSMatthew Dillon mirror.shared_uuid = pfs.ondisk->shared_uuid; 869a7fbbf91SMatthew Dillon mirror.size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup); 870a7fbbf91SMatthew Dillon if (mirror.size <= 0) 871a7fbbf91SMatthew Dillon break; 872a7fbbf91SMatthew Dillon if (ioctl(fd, HAMMERIOC_MIRROR_WRITE, &mirror) < 0) { 873a7fbbf91SMatthew Dillon fprintf(stderr, "Mirror-write %s failed: %s\n", 874a7fbbf91SMatthew Dillon filesystem, strerror(errno)); 875a7fbbf91SMatthew Dillon exit(1); 876a7fbbf91SMatthew Dillon } 87717dd83bcSMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) { 87817dd83bcSMatthew Dillon fprintf(stderr, 87917dd83bcSMatthew Dillon "Mirror-write %s fatal error %d\n", 88017dd83bcSMatthew Dillon filesystem, mirror.head.error); 88117dd83bcSMatthew Dillon exit(1); 88217dd83bcSMatthew Dillon } 883243ca327SMatthew Dillon #if 0 884a7fbbf91SMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_INTR) { 885a7fbbf91SMatthew Dillon fprintf(stderr, 886a7fbbf91SMatthew Dillon "Mirror-write %s interrupted by timer at" 887243ca327SMatthew Dillon " %016llx\n", 888a7fbbf91SMatthew Dillon filesystem, 889243ca327SMatthew Dillon mirror.key_cur.obj_id); 890a7fbbf91SMatthew Dillon exit(0); 891a7fbbf91SMatthew Dillon } 892243ca327SMatthew Dillon #endif 893a7fbbf91SMatthew Dillon } 894243ca327SMatthew Dillon 895243ca327SMatthew Dillon /* 896243ca327SMatthew Dillon * Read and process the termination sync record. 897243ca327SMatthew Dillon */ 898243ca327SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 8999dc76cb1SMatthew Dillon 9009dc76cb1SMatthew Dillon if (mrec && mrec->head.type == HAMMER_MREC_TYPE_TERM) { 90148eadef9SMatthew Dillon fprintf(stderr, "Mirror-write: received termination request\n"); 902*764d0c8cSTomohiro Kusumi relpfs(fd, &pfs); 90348eadef9SMatthew Dillon free(mrec); 904*764d0c8cSTomohiro Kusumi free(buf); 9059dc76cb1SMatthew Dillon return; 9069dc76cb1SMatthew Dillon } 9079dc76cb1SMatthew Dillon 90817dd83bcSMatthew Dillon if (mrec == NULL || 90948eadef9SMatthew Dillon (mrec->head.type != HAMMER_MREC_TYPE_SYNC && 91048eadef9SMatthew Dillon mrec->head.type != HAMMER_MREC_TYPE_IDLE) || 91117dd83bcSMatthew Dillon mrec->head.rec_size != sizeof(mrec->sync)) { 912243ca327SMatthew Dillon fprintf(stderr, "Mirror-write %s: Did not get termination " 91317dd83bcSMatthew Dillon "sync record, or rec_size is wrong rt=%d\n", 914db7dd3cfSMatthew Dillon filesystem, 915a446fca6SSascha Wildner (mrec ? (int)mrec->head.type : -1)); 916da44aa75SMatthew Dillon exit(1); 917243ca327SMatthew Dillon } 918243ca327SMatthew Dillon 919243ca327SMatthew Dillon /* 920243ca327SMatthew Dillon * Update the PFS info on the target so the user has visibility 92148eadef9SMatthew Dillon * into the new snapshot, and sync the target filesystem. 922243ca327SMatthew Dillon */ 92348eadef9SMatthew Dillon if (mrec->head.type == HAMMER_MREC_TYPE_SYNC) { 924d4e5b69bSMatthew Dillon update_pfs_snapshot(fd, mirror.tid_end, pfs.pfs_id); 925243ca327SMatthew Dillon 926243ca327SMatthew Dillon bzero(&synctid, sizeof(synctid)); 927243ca327SMatthew Dillon synctid.op = HAMMER_SYNCTID_SYNC2; 928243ca327SMatthew Dillon ioctl(fd, HAMMERIOC_SYNCTID, &synctid); 929243ca327SMatthew Dillon 93048eadef9SMatthew Dillon if (VerboseOpt >= 2) { 93148eadef9SMatthew Dillon fprintf(stderr, "Mirror-write %s: succeeded\n", 93248eadef9SMatthew Dillon filesystem); 93348eadef9SMatthew Dillon } 93448eadef9SMatthew Dillon } 93548eadef9SMatthew Dillon 93648eadef9SMatthew Dillon free(mrec); 93748eadef9SMatthew Dillon mrec = NULL; 938243ca327SMatthew Dillon 939243ca327SMatthew Dillon /* 940243ca327SMatthew Dillon * Report back to the originator. 941243ca327SMatthew Dillon */ 942243ca327SMatthew Dillon if (TwoWayPipeOpt) { 94317dd83bcSMatthew Dillon mrec_tmp.update.tid = mirror.tid_end; 944243ca327SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_UPDATE, 94517dd83bcSMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.update)); 946243ca327SMatthew Dillon } else { 947a276dc6bSMatthew Dillon printf("Source can update synctid to 0x%016jx\n", 948a276dc6bSMatthew Dillon (uintmax_t)mirror.tid_end); 949243ca327SMatthew Dillon } 95048eadef9SMatthew Dillon relpfs(fd, &pfs); 95148eadef9SMatthew Dillon goto again; 952243ca327SMatthew Dillon } 953243ca327SMatthew Dillon 954243ca327SMatthew Dillon void 9559f1b0121SAntonio Huete Jimenez hammer_cmd_mirror_dump(char **av, int ac) 956243ca327SMatthew Dillon { 957243ca327SMatthew Dillon char *buf = malloc(SERIALBUF_SIZE); 95817dd83bcSMatthew Dillon struct hammer_ioc_mrecord_head pickup; 95917dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 960243ca327SMatthew Dillon int error; 961243ca327SMatthew Dillon int size; 96217dd83bcSMatthew Dillon int offset; 96317dd83bcSMatthew Dillon int bytes; 9649f1b0121SAntonio Huete Jimenez int header_only = 0; 9659f1b0121SAntonio Huete Jimenez 9669f1b0121SAntonio Huete Jimenez if (ac == 1 && strcmp(*av, "header") == 0) 9679f1b0121SAntonio Huete Jimenez header_only = 1; 9689f1b0121SAntonio Huete Jimenez else if (ac != 0) 9699f1b0121SAntonio Huete Jimenez mirror_usage(1); 970243ca327SMatthew Dillon 971243ca327SMatthew Dillon /* 972243ca327SMatthew Dillon * Read and process the PFS header 973243ca327SMatthew Dillon */ 974243ca327SMatthew Dillon pickup.signature = 0; 975243ca327SMatthew Dillon pickup.type = 0; 976243ca327SMatthew Dillon 977243ca327SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 978243ca327SMatthew Dillon 9799f1b0121SAntonio Huete Jimenez /* 9809f1b0121SAntonio Huete Jimenez * Dump the PFS header. mirror-dump takes its input from the output 9819f1b0121SAntonio Huete Jimenez * of a mirror-read so getpfs() can't be used to get a fd to be passed 9829f1b0121SAntonio Huete Jimenez * to dump_pfsd(). 9839f1b0121SAntonio Huete Jimenez */ 9849f1b0121SAntonio Huete Jimenez if (header_only && mrec != NULL) { 9859f1b0121SAntonio Huete Jimenez dump_pfsd(&mrec->pfs.pfsd, -1); 986cc71ff00STomohiro Kusumi free(mrec); 987*764d0c8cSTomohiro Kusumi free(buf); 9889f1b0121SAntonio Huete Jimenez return; 9899f1b0121SAntonio Huete Jimenez } 990cc71ff00STomohiro Kusumi free(mrec); 9919f1b0121SAntonio Huete Jimenez 99239e88285SMatthew Dillon again: 993243ca327SMatthew Dillon /* 994243ca327SMatthew Dillon * Read and process bulk records 995243ca327SMatthew Dillon */ 996243ca327SMatthew Dillon for (;;) { 997243ca327SMatthew Dillon size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup); 998243ca327SMatthew Dillon if (size <= 0) 999243ca327SMatthew Dillon break; 100017dd83bcSMatthew Dillon offset = 0; 100117dd83bcSMatthew Dillon while (offset < size) { 100217dd83bcSMatthew Dillon mrec = (void *)((char *)buf + offset); 100317dd83bcSMatthew Dillon bytes = HAMMER_HEAD_DOALIGN(mrec->head.rec_size); 100417dd83bcSMatthew Dillon if (offset + bytes > size) { 100517dd83bcSMatthew Dillon fprintf(stderr, "Misaligned record\n"); 100617dd83bcSMatthew Dillon exit(1); 100717dd83bcSMatthew Dillon } 100817dd83bcSMatthew Dillon 100985a8e8a7SMatthew Dillon switch(mrec->head.type & HAMMER_MRECF_TYPE_MASK) { 101085a8e8a7SMatthew Dillon case HAMMER_MREC_TYPE_REC_BADCRC: 101117dd83bcSMatthew Dillon case HAMMER_MREC_TYPE_REC: 1012a276dc6bSMatthew Dillon printf("Record obj=%016jx key=%016jx " 101385a8e8a7SMatthew Dillon "rt=%02x ot=%02x", 1014a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.obj_id, 1015a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.key, 101617dd83bcSMatthew Dillon mrec->rec.leaf.base.rec_type, 101717dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_type); 101885a8e8a7SMatthew Dillon if (mrec->head.type == 101985a8e8a7SMatthew Dillon HAMMER_MREC_TYPE_REC_BADCRC) { 102085a8e8a7SMatthew Dillon printf(" (BAD CRC)"); 102185a8e8a7SMatthew Dillon } 102285a8e8a7SMatthew Dillon printf("\n"); 1023a276dc6bSMatthew Dillon printf(" tids %016jx:%016jx data=%d\n", 1024a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.create_tid, 1025a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.delete_tid, 102617dd83bcSMatthew Dillon mrec->rec.leaf.data_len); 102717dd83bcSMatthew Dillon break; 102817dd83bcSMatthew Dillon case HAMMER_MREC_TYPE_PASS: 1029a276dc6bSMatthew Dillon printf("Pass obj=%016jx key=%016jx " 103017dd83bcSMatthew Dillon "rt=%02x ot=%02x\n", 1031a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.obj_id, 1032a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.key, 103317dd83bcSMatthew Dillon mrec->rec.leaf.base.rec_type, 103417dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_type); 1035a276dc6bSMatthew Dillon printf(" tids %016jx:%016jx data=%d\n", 1036a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.create_tid, 1037a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.delete_tid, 103817dd83bcSMatthew Dillon mrec->rec.leaf.data_len); 103917dd83bcSMatthew Dillon break; 104017dd83bcSMatthew Dillon case HAMMER_MREC_TYPE_SKIP: 1041a276dc6bSMatthew Dillon printf("Skip obj=%016jx key=%016jx rt=%02x to\n" 1042a276dc6bSMatthew Dillon " obj=%016jx key=%016jx rt=%02x\n", 1043a276dc6bSMatthew Dillon (uintmax_t)mrec->skip.skip_beg.obj_id, 1044a276dc6bSMatthew Dillon (uintmax_t)mrec->skip.skip_beg.key, 104517dd83bcSMatthew Dillon mrec->skip.skip_beg.rec_type, 1046a276dc6bSMatthew Dillon (uintmax_t)mrec->skip.skip_end.obj_id, 1047a276dc6bSMatthew Dillon (uintmax_t)mrec->skip.skip_end.key, 104817dd83bcSMatthew Dillon mrec->skip.skip_end.rec_type); 104917dd83bcSMatthew Dillon default: 105017dd83bcSMatthew Dillon break; 105117dd83bcSMatthew Dillon } 105217dd83bcSMatthew Dillon offset += bytes; 1053243ca327SMatthew Dillon } 1054243ca327SMatthew Dillon } 1055243ca327SMatthew Dillon 1056243ca327SMatthew Dillon /* 1057243ca327SMatthew Dillon * Read and process the termination sync record. 1058243ca327SMatthew Dillon */ 1059243ca327SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 106048eadef9SMatthew Dillon if (mrec == NULL || 106148eadef9SMatthew Dillon (mrec->head.type != HAMMER_MREC_TYPE_SYNC && 106248eadef9SMatthew Dillon mrec->head.type != HAMMER_MREC_TYPE_IDLE) 106348eadef9SMatthew Dillon ) { 1064243ca327SMatthew Dillon fprintf(stderr, "Mirror-dump: Did not get termination " 1065243ca327SMatthew Dillon "sync record\n"); 1066243ca327SMatthew Dillon } 1067cc71ff00STomohiro Kusumi free(mrec); 106839e88285SMatthew Dillon 106939e88285SMatthew Dillon /* 107039e88285SMatthew Dillon * Continue with more batches until EOF. 107139e88285SMatthew Dillon */ 107239e88285SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 1073cc71ff00STomohiro Kusumi if (mrec) { 1074cc71ff00STomohiro Kusumi free(mrec); 107539e88285SMatthew Dillon goto again; 1076a7fbbf91SMatthew Dillon } 1077*764d0c8cSTomohiro Kusumi free(buf); 1078cc71ff00STomohiro Kusumi } 1079a7fbbf91SMatthew Dillon 1080a7fbbf91SMatthew Dillon void 108148eadef9SMatthew Dillon hammer_cmd_mirror_copy(char **av, int ac, int streaming) 1082a7fbbf91SMatthew Dillon { 108334ebae70SMatthew Dillon pid_t pid1; 108434ebae70SMatthew Dillon pid_t pid2; 108534ebae70SMatthew Dillon int fds[2]; 108608b71f9fSMatthew Dillon const char *xav[32]; 1087243ca327SMatthew Dillon char tbuf[16]; 1088e2c596b1SChris Turner char *sh, *user, *host, *rfs; 1089243ca327SMatthew Dillon int xac; 109034ebae70SMatthew Dillon 109134ebae70SMatthew Dillon if (ac != 2) 109234ebae70SMatthew Dillon mirror_usage(1); 109334ebae70SMatthew Dillon 1094e7f926a5SMatthew Dillon TwoWayPipeOpt = 1; 10950bd7a37cSMatthew Dillon signal(SIGPIPE, SIG_IGN); 1096e7f926a5SMatthew Dillon 1097e7f926a5SMatthew Dillon again: 109834ebae70SMatthew Dillon if (pipe(fds) < 0) { 109934ebae70SMatthew Dillon perror("pipe"); 110034ebae70SMatthew Dillon exit(1); 110134ebae70SMatthew Dillon } 110234ebae70SMatthew Dillon 110334ebae70SMatthew Dillon /* 110434ebae70SMatthew Dillon * Source 110534ebae70SMatthew Dillon */ 110634ebae70SMatthew Dillon if ((pid1 = fork()) == 0) { 11070bd7a37cSMatthew Dillon signal(SIGPIPE, SIG_DFL); 110834ebae70SMatthew Dillon dup2(fds[0], 0); 110934ebae70SMatthew Dillon dup2(fds[0], 1); 111034ebae70SMatthew Dillon close(fds[0]); 111134ebae70SMatthew Dillon close(fds[1]); 1112e2c596b1SChris Turner if ((rfs = strchr(av[0], ':')) != NULL) { 1113243ca327SMatthew Dillon xac = 0; 1114e2c596b1SChris Turner 1115e2c596b1SChris Turner if((sh = getenv("HAMMER_RSH")) == NULL) 1116243ca327SMatthew Dillon xav[xac++] = "ssh"; 1117e2c596b1SChris Turner else 1118e2c596b1SChris Turner xav[xac++] = sh; 1119e2c596b1SChris Turner 11203a998207SMatthew Dillon if (CompressOpt) 11213a998207SMatthew Dillon xav[xac++] = "-C"; 1122e2c596b1SChris Turner 1123e2c596b1SChris Turner if ((host = strchr(av[0], '@')) != NULL) { 1124e2c596b1SChris Turner user = strndup( av[0], (host++ - av[0])); 1125e2c596b1SChris Turner host = strndup( host, (rfs++ - host)); 1126e2c596b1SChris Turner xav[xac++] = "-l"; 1127e2c596b1SChris Turner xav[xac++] = user; 1128e2c596b1SChris Turner xav[xac++] = host; 1129e2c596b1SChris Turner } 1130e2c596b1SChris Turner else { 1131e2c596b1SChris Turner host = strndup( av[0], (rfs++ - av[0])); 1132e2c596b1SChris Turner user = NULL; 1133e2c596b1SChris Turner xav[xac++] = host; 1134e2c596b1SChris Turner } 1135e2c596b1SChris Turner 1136e2c596b1SChris Turner 11376c45ca3eSMatthew Dillon if (SshPort) { 11386c45ca3eSMatthew Dillon xav[xac++] = "-p"; 11396c45ca3eSMatthew Dillon xav[xac++] = SshPort; 11406c45ca3eSMatthew Dillon } 1141e2c596b1SChris Turner 1142243ca327SMatthew Dillon xav[xac++] = "hammer"; 114348eadef9SMatthew Dillon 114448eadef9SMatthew Dillon switch(VerboseOpt) { 114548eadef9SMatthew Dillon case 0: 114648eadef9SMatthew Dillon break; 114748eadef9SMatthew Dillon case 1: 1148243ca327SMatthew Dillon xav[xac++] = "-v"; 114948eadef9SMatthew Dillon break; 115048eadef9SMatthew Dillon case 2: 115148eadef9SMatthew Dillon xav[xac++] = "-vv"; 115248eadef9SMatthew Dillon break; 115348eadef9SMatthew Dillon default: 115448eadef9SMatthew Dillon xav[xac++] = "-vvv"; 115548eadef9SMatthew Dillon break; 115648eadef9SMatthew Dillon } 115707485271SMichael Neumann if (ForceYesOpt) { 115807485271SMichael Neumann xav[xac++] = "-y"; 115907485271SMichael Neumann } 1160243ca327SMatthew Dillon xav[xac++] = "-2"; 1161243ca327SMatthew Dillon if (TimeoutOpt) { 1162243ca327SMatthew Dillon snprintf(tbuf, sizeof(tbuf), "%d", TimeoutOpt); 1163243ca327SMatthew Dillon xav[xac++] = "-t"; 1164243ca327SMatthew Dillon xav[xac++] = tbuf; 1165243ca327SMatthew Dillon } 116608b71f9fSMatthew Dillon if (SplitupOptStr) { 116708b71f9fSMatthew Dillon xav[xac++] = "-S"; 116808b71f9fSMatthew Dillon xav[xac++] = SplitupOptStr; 116908b71f9fSMatthew Dillon } 117048eadef9SMatthew Dillon if (streaming) 1171901f434aSMatthew Dillon xav[xac++] = "mirror-read-stream"; 117248eadef9SMatthew Dillon else 1173243ca327SMatthew Dillon xav[xac++] = "mirror-read"; 1174e2c596b1SChris Turner xav[xac++] = rfs; 1175243ca327SMatthew Dillon xav[xac++] = NULL; 1176e2c596b1SChris Turner execvp(*xav, (void *)xav); 117734ebae70SMatthew Dillon } else { 117848eadef9SMatthew Dillon hammer_cmd_mirror_read(av, 1, streaming); 1179243ca327SMatthew Dillon fflush(stdout); 1180243ca327SMatthew Dillon fflush(stderr); 118134ebae70SMatthew Dillon } 118253d93cc7SMatthew Dillon _exit(1); 118334ebae70SMatthew Dillon } 118434ebae70SMatthew Dillon 118534ebae70SMatthew Dillon /* 118634ebae70SMatthew Dillon * Target 118734ebae70SMatthew Dillon */ 118834ebae70SMatthew Dillon if ((pid2 = fork()) == 0) { 11890bd7a37cSMatthew Dillon signal(SIGPIPE, SIG_DFL); 119034ebae70SMatthew Dillon dup2(fds[1], 0); 119134ebae70SMatthew Dillon dup2(fds[1], 1); 119234ebae70SMatthew Dillon close(fds[0]); 119334ebae70SMatthew Dillon close(fds[1]); 1194e2c596b1SChris Turner if ((rfs = strchr(av[1], ':')) != NULL) { 1195243ca327SMatthew Dillon xac = 0; 1196e2c596b1SChris Turner 1197e2c596b1SChris Turner if((sh = getenv("HAMMER_RSH")) == NULL) 1198243ca327SMatthew Dillon xav[xac++] = "ssh"; 1199e2c596b1SChris Turner else 1200e2c596b1SChris Turner xav[xac++] = sh; 1201e2c596b1SChris Turner 12023a998207SMatthew Dillon if (CompressOpt) 12033a998207SMatthew Dillon xav[xac++] = "-C"; 1204e2c596b1SChris Turner 1205e2c596b1SChris Turner if ((host = strchr(av[1], '@')) != NULL) { 1206e2c596b1SChris Turner user = strndup( av[1], (host++ - av[1])); 1207e2c596b1SChris Turner host = strndup( host, (rfs++ - host)); 1208e2c596b1SChris Turner xav[xac++] = "-l"; 1209e2c596b1SChris Turner xav[xac++] = user; 1210e2c596b1SChris Turner xav[xac++] = host; 1211e2c596b1SChris Turner } 1212e2c596b1SChris Turner else { 1213e2c596b1SChris Turner host = strndup( av[1], (rfs++ - av[1])); 1214e2c596b1SChris Turner user = NULL; 1215e2c596b1SChris Turner xav[xac++] = host; 1216e2c596b1SChris Turner } 1217e2c596b1SChris Turner 12186c45ca3eSMatthew Dillon if (SshPort) { 12196c45ca3eSMatthew Dillon xav[xac++] = "-p"; 12206c45ca3eSMatthew Dillon xav[xac++] = SshPort; 12216c45ca3eSMatthew Dillon } 1222e2c596b1SChris Turner 1223243ca327SMatthew Dillon xav[xac++] = "hammer"; 122448eadef9SMatthew Dillon 122548eadef9SMatthew Dillon switch(VerboseOpt) { 122648eadef9SMatthew Dillon case 0: 122748eadef9SMatthew Dillon break; 122848eadef9SMatthew Dillon case 1: 1229243ca327SMatthew Dillon xav[xac++] = "-v"; 123048eadef9SMatthew Dillon break; 123148eadef9SMatthew Dillon case 2: 123248eadef9SMatthew Dillon xav[xac++] = "-vv"; 123348eadef9SMatthew Dillon break; 123448eadef9SMatthew Dillon default: 123548eadef9SMatthew Dillon xav[xac++] = "-vvv"; 123648eadef9SMatthew Dillon break; 123748eadef9SMatthew Dillon } 123807485271SMichael Neumann if (ForceYesOpt) { 123907485271SMichael Neumann xav[xac++] = "-y"; 124007485271SMichael Neumann } 1241243ca327SMatthew Dillon xav[xac++] = "-2"; 1242243ca327SMatthew Dillon xav[xac++] = "mirror-write"; 1243e2c596b1SChris Turner xav[xac++] = rfs; 1244243ca327SMatthew Dillon xav[xac++] = NULL; 1245e2c596b1SChris Turner execvp(*xav, (void *)xav); 124634ebae70SMatthew Dillon } else { 124734ebae70SMatthew Dillon hammer_cmd_mirror_write(av + 1, 1); 1248243ca327SMatthew Dillon fflush(stdout); 1249243ca327SMatthew Dillon fflush(stderr); 125034ebae70SMatthew Dillon } 125153d93cc7SMatthew Dillon _exit(1); 125234ebae70SMatthew Dillon } 125334ebae70SMatthew Dillon close(fds[0]); 125434ebae70SMatthew Dillon close(fds[1]); 125534ebae70SMatthew Dillon 125634ebae70SMatthew Dillon while (waitpid(pid1, NULL, 0) <= 0) 125734ebae70SMatthew Dillon ; 125834ebae70SMatthew Dillon while (waitpid(pid2, NULL, 0) <= 0) 125934ebae70SMatthew Dillon ; 1260e7f926a5SMatthew Dillon 1261e7f926a5SMatthew Dillon /* 1262e7f926a5SMatthew Dillon * If the link is lost restart 1263e7f926a5SMatthew Dillon */ 1264e7f926a5SMatthew Dillon if (streaming) { 1265e7f926a5SMatthew Dillon if (VerboseOpt) { 1266e7f926a5SMatthew Dillon fprintf(stderr, "\nLost Link\n"); 1267e7f926a5SMatthew Dillon fflush(stderr); 1268e7f926a5SMatthew Dillon } 12690bd7a37cSMatthew Dillon sleep(15 + DelayOpt); 1270e7f926a5SMatthew Dillon goto again; 1271e7f926a5SMatthew Dillon } 1272e7f926a5SMatthew Dillon 1273a7fbbf91SMatthew Dillon } 1274a7fbbf91SMatthew Dillon 1275243ca327SMatthew Dillon /* 1276243ca327SMatthew Dillon * Read and return multiple mrecords 1277243ca327SMatthew Dillon */ 1278a7fbbf91SMatthew Dillon static int 127917dd83bcSMatthew Dillon read_mrecords(int fd, char *buf, u_int size, hammer_ioc_mrecord_head_t pickup) 1280a7fbbf91SMatthew Dillon { 128117dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 1282a7fbbf91SMatthew Dillon u_int count; 1283a7fbbf91SMatthew Dillon size_t n; 1284a7fbbf91SMatthew Dillon size_t i; 128517dd83bcSMatthew Dillon size_t bytes; 128685a8e8a7SMatthew Dillon int type; 1287a7fbbf91SMatthew Dillon 1288a7fbbf91SMatthew Dillon count = 0; 1289a7fbbf91SMatthew Dillon while (size - count >= HAMMER_MREC_HEADSIZE) { 1290a7fbbf91SMatthew Dillon /* 1291a7fbbf91SMatthew Dillon * Cached the record header in case we run out of buffer 1292a7fbbf91SMatthew Dillon * space. 1293a7fbbf91SMatthew Dillon */ 129417dd83bcSMatthew Dillon fflush(stdout); 1295a7fbbf91SMatthew Dillon if (pickup->signature == 0) { 1296a7fbbf91SMatthew Dillon for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) { 1297a7fbbf91SMatthew Dillon i = read(fd, (char *)pickup + n, 1298a7fbbf91SMatthew Dillon HAMMER_MREC_HEADSIZE - n); 1299a7fbbf91SMatthew Dillon if (i <= 0) 1300a7fbbf91SMatthew Dillon break; 1301a7fbbf91SMatthew Dillon } 1302a7fbbf91SMatthew Dillon if (n == 0) 1303a7fbbf91SMatthew Dillon break; 1304a7fbbf91SMatthew Dillon if (n != HAMMER_MREC_HEADSIZE) { 1305a7fbbf91SMatthew Dillon fprintf(stderr, "read_mrecords: short read on pipe\n"); 1306a7fbbf91SMatthew Dillon exit(1); 1307a7fbbf91SMatthew Dillon } 1308a7fbbf91SMatthew Dillon if (pickup->signature != HAMMER_IOC_MIRROR_SIGNATURE) { 130934bb69d8SThomas Nikolajsen fprintf(stderr, "read_mrecords: malformed record on pipe, " 131034bb69d8SThomas Nikolajsen "bad signature\n"); 1311a7fbbf91SMatthew Dillon exit(1); 1312a7fbbf91SMatthew Dillon } 1313a7fbbf91SMatthew Dillon } 1314a7fbbf91SMatthew Dillon if (pickup->rec_size < HAMMER_MREC_HEADSIZE || 131517dd83bcSMatthew Dillon pickup->rec_size > sizeof(*mrec) + HAMMER_XBUFSIZE) { 131634bb69d8SThomas Nikolajsen fprintf(stderr, "read_mrecords: malformed record on pipe, " 131734bb69d8SThomas Nikolajsen "illegal rec_size\n"); 1318a7fbbf91SMatthew Dillon exit(1); 1319a7fbbf91SMatthew Dillon } 1320a7fbbf91SMatthew Dillon 1321a7fbbf91SMatthew Dillon /* 1322a7fbbf91SMatthew Dillon * Stop if we have insufficient space for the record and data. 1323a7fbbf91SMatthew Dillon */ 132417dd83bcSMatthew Dillon bytes = HAMMER_HEAD_DOALIGN(pickup->rec_size); 132517dd83bcSMatthew Dillon if (size - count < bytes) 1326a7fbbf91SMatthew Dillon break; 1327a7fbbf91SMatthew Dillon 1328a7fbbf91SMatthew Dillon /* 132985a8e8a7SMatthew Dillon * Stop if the record type is not a REC, SKIP, or PASS, 133085a8e8a7SMatthew Dillon * which are the only types the ioctl supports. Other types 133185a8e8a7SMatthew Dillon * are used only by the userland protocol. 133285a8e8a7SMatthew Dillon * 133385a8e8a7SMatthew Dillon * Ignore all flags. 1334243ca327SMatthew Dillon */ 133585a8e8a7SMatthew Dillon type = pickup->type & HAMMER_MRECF_TYPE_LOMASK; 133685a8e8a7SMatthew Dillon if (type != HAMMER_MREC_TYPE_PFSD && 133785a8e8a7SMatthew Dillon type != HAMMER_MREC_TYPE_REC && 133885a8e8a7SMatthew Dillon type != HAMMER_MREC_TYPE_SKIP && 133985a8e8a7SMatthew Dillon type != HAMMER_MREC_TYPE_PASS) { 1340243ca327SMatthew Dillon break; 134117dd83bcSMatthew Dillon } 1342243ca327SMatthew Dillon 1343243ca327SMatthew Dillon /* 1344a7fbbf91SMatthew Dillon * Read the remainder and clear the pickup signature. 1345a7fbbf91SMatthew Dillon */ 134617dd83bcSMatthew Dillon for (n = HAMMER_MREC_HEADSIZE; n < bytes; n += i) { 134717dd83bcSMatthew Dillon i = read(fd, buf + count + n, bytes - n); 1348a7fbbf91SMatthew Dillon if (i <= 0) 1349a7fbbf91SMatthew Dillon break; 1350a7fbbf91SMatthew Dillon } 135117dd83bcSMatthew Dillon if (n != bytes) { 1352a7fbbf91SMatthew Dillon fprintf(stderr, "read_mrecords: short read on pipe\n"); 1353a7fbbf91SMatthew Dillon exit(1); 1354a7fbbf91SMatthew Dillon } 135517dd83bcSMatthew Dillon 135617dd83bcSMatthew Dillon bcopy(pickup, buf + count, HAMMER_MREC_HEADSIZE); 135717dd83bcSMatthew Dillon pickup->signature = 0; 135817dd83bcSMatthew Dillon pickup->type = 0; 135917dd83bcSMatthew Dillon mrec = (void *)(buf + count); 136017dd83bcSMatthew Dillon 136117dd83bcSMatthew Dillon /* 136217dd83bcSMatthew Dillon * Validate the completed record 136317dd83bcSMatthew Dillon */ 136417dd83bcSMatthew Dillon if (mrec->head.rec_crc != 136517dd83bcSMatthew Dillon crc32((char *)mrec + HAMMER_MREC_CRCOFF, 136617dd83bcSMatthew Dillon mrec->head.rec_size - HAMMER_MREC_CRCOFF)) { 136717dd83bcSMatthew Dillon fprintf(stderr, "read_mrecords: malformed record " 136817dd83bcSMatthew Dillon "on pipe, bad crc\n"); 136917dd83bcSMatthew Dillon exit(1); 1370a7fbbf91SMatthew Dillon } 1371a7fbbf91SMatthew Dillon 137217dd83bcSMatthew Dillon /* 137385a8e8a7SMatthew Dillon * If its a B-Tree record validate the data crc. 137485a8e8a7SMatthew Dillon * 137585a8e8a7SMatthew Dillon * NOTE: If the VFS passes us an explicitly errorde mrec 137685a8e8a7SMatthew Dillon * we just pass it through. 137717dd83bcSMatthew Dillon */ 137885a8e8a7SMatthew Dillon type = mrec->head.type & HAMMER_MRECF_TYPE_MASK; 137985a8e8a7SMatthew Dillon 138085a8e8a7SMatthew Dillon if (type == HAMMER_MREC_TYPE_REC) { 138117dd83bcSMatthew Dillon if (mrec->head.rec_size < 138217dd83bcSMatthew Dillon sizeof(mrec->rec) + mrec->rec.leaf.data_len) { 138317dd83bcSMatthew Dillon fprintf(stderr, 138417dd83bcSMatthew Dillon "read_mrecords: malformed record on " 138517dd83bcSMatthew Dillon "pipe, illegal element data_len\n"); 138617dd83bcSMatthew Dillon exit(1); 138717dd83bcSMatthew Dillon } 138817dd83bcSMatthew Dillon if (mrec->rec.leaf.data_len && 138917dd83bcSMatthew Dillon mrec->rec.leaf.data_offset && 139017dd83bcSMatthew Dillon hammer_crc_test_leaf(&mrec->rec + 1, &mrec->rec.leaf) == 0) { 139117dd83bcSMatthew Dillon fprintf(stderr, 139217dd83bcSMatthew Dillon "read_mrecords: data_crc did not " 1393a276dc6bSMatthew Dillon "match data! obj=%016jx key=%016jx\n", 1394a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.obj_id, 1395a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.key); 139617dd83bcSMatthew Dillon fprintf(stderr, 139717dd83bcSMatthew Dillon "continuing, but there are problems\n"); 139817dd83bcSMatthew Dillon } 139917dd83bcSMatthew Dillon } 140017dd83bcSMatthew Dillon count += bytes; 1401a7fbbf91SMatthew Dillon } 1402a7fbbf91SMatthew Dillon return(count); 1403a7fbbf91SMatthew Dillon } 1404a7fbbf91SMatthew Dillon 140534ebae70SMatthew Dillon /* 140617dd83bcSMatthew Dillon * Read and return a single mrecord. 1407243ca327SMatthew Dillon */ 1408243ca327SMatthew Dillon static 140917dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t 141017dd83bcSMatthew Dillon read_mrecord(int fdin, int *errorp, hammer_ioc_mrecord_head_t pickup) 1411243ca327SMatthew Dillon { 141217dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 141317dd83bcSMatthew Dillon struct hammer_ioc_mrecord_head mrechd; 1414243ca327SMatthew Dillon size_t bytes; 1415243ca327SMatthew Dillon size_t n; 1416243ca327SMatthew Dillon size_t i; 1417243ca327SMatthew Dillon 1418243ca327SMatthew Dillon if (pickup && pickup->type != 0) { 1419243ca327SMatthew Dillon mrechd = *pickup; 1420243ca327SMatthew Dillon pickup->signature = 0; 1421243ca327SMatthew Dillon pickup->type = 0; 1422243ca327SMatthew Dillon n = HAMMER_MREC_HEADSIZE; 1423243ca327SMatthew Dillon } else { 1424243ca327SMatthew Dillon /* 1425243ca327SMatthew Dillon * Read in the PFSD header from the sender. 1426243ca327SMatthew Dillon */ 1427243ca327SMatthew Dillon for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) { 1428243ca327SMatthew Dillon i = read(fdin, (char *)&mrechd + n, HAMMER_MREC_HEADSIZE - n); 1429243ca327SMatthew Dillon if (i <= 0) 1430243ca327SMatthew Dillon break; 1431243ca327SMatthew Dillon } 1432243ca327SMatthew Dillon if (n == 0) { 1433243ca327SMatthew Dillon *errorp = 0; /* EOF */ 1434243ca327SMatthew Dillon return(NULL); 1435243ca327SMatthew Dillon } 1436243ca327SMatthew Dillon if (n != HAMMER_MREC_HEADSIZE) { 1437243ca327SMatthew Dillon fprintf(stderr, "short read of mrecord header\n"); 1438243ca327SMatthew Dillon *errorp = EPIPE; 1439243ca327SMatthew Dillon return(NULL); 1440243ca327SMatthew Dillon } 1441243ca327SMatthew Dillon } 1442243ca327SMatthew Dillon if (mrechd.signature != HAMMER_IOC_MIRROR_SIGNATURE) { 1443243ca327SMatthew Dillon fprintf(stderr, "read_mrecord: bad signature\n"); 1444243ca327SMatthew Dillon *errorp = EINVAL; 1445243ca327SMatthew Dillon return(NULL); 1446243ca327SMatthew Dillon } 144717dd83bcSMatthew Dillon bytes = HAMMER_HEAD_DOALIGN(mrechd.rec_size); 144817dd83bcSMatthew Dillon assert(bytes >= sizeof(mrechd)); 1449243ca327SMatthew Dillon mrec = malloc(bytes); 145017dd83bcSMatthew Dillon mrec->head = mrechd; 145117dd83bcSMatthew Dillon 1452243ca327SMatthew Dillon while (n < bytes) { 1453243ca327SMatthew Dillon i = read(fdin, (char *)mrec + n, bytes - n); 1454243ca327SMatthew Dillon if (i <= 0) 1455243ca327SMatthew Dillon break; 1456243ca327SMatthew Dillon n += i; 1457243ca327SMatthew Dillon } 1458243ca327SMatthew Dillon if (n != bytes) { 1459243ca327SMatthew Dillon fprintf(stderr, "read_mrecord: short read on payload\n"); 1460243ca327SMatthew Dillon *errorp = EPIPE; 1461243ca327SMatthew Dillon return(NULL); 1462243ca327SMatthew Dillon } 146317dd83bcSMatthew Dillon if (mrec->head.rec_crc != 146417dd83bcSMatthew Dillon crc32((char *)mrec + HAMMER_MREC_CRCOFF, 146517dd83bcSMatthew Dillon mrec->head.rec_size - HAMMER_MREC_CRCOFF)) { 1466243ca327SMatthew Dillon fprintf(stderr, "read_mrecord: bad CRC\n"); 1467243ca327SMatthew Dillon *errorp = EINVAL; 1468243ca327SMatthew Dillon return(NULL); 1469243ca327SMatthew Dillon } 1470243ca327SMatthew Dillon *errorp = 0; 1471243ca327SMatthew Dillon return(mrec); 1472243ca327SMatthew Dillon } 1473243ca327SMatthew Dillon 1474243ca327SMatthew Dillon static 1475243ca327SMatthew Dillon void 147617dd83bcSMatthew Dillon write_mrecord(int fdout, u_int32_t type, hammer_ioc_mrecord_any_t mrec, 147717dd83bcSMatthew Dillon int bytes) 1478243ca327SMatthew Dillon { 147917dd83bcSMatthew Dillon char zbuf[HAMMER_HEAD_ALIGN]; 148017dd83bcSMatthew Dillon int pad; 1481243ca327SMatthew Dillon 148217dd83bcSMatthew Dillon pad = HAMMER_HEAD_DOALIGN(bytes) - bytes; 148317dd83bcSMatthew Dillon 148417dd83bcSMatthew Dillon assert(bytes >= (int)sizeof(mrec->head)); 148517dd83bcSMatthew Dillon bzero(&mrec->head, sizeof(mrec->head)); 148617dd83bcSMatthew Dillon mrec->head.signature = HAMMER_IOC_MIRROR_SIGNATURE; 148717dd83bcSMatthew Dillon mrec->head.type = type; 148817dd83bcSMatthew Dillon mrec->head.rec_size = bytes; 148917dd83bcSMatthew Dillon mrec->head.rec_crc = crc32((char *)mrec + HAMMER_MREC_CRCOFF, 149017dd83bcSMatthew Dillon bytes - HAMMER_MREC_CRCOFF); 149117dd83bcSMatthew Dillon if (write(fdout, mrec, bytes) != bytes) { 1492243ca327SMatthew Dillon fprintf(stderr, "write_mrecord: error %d (%s)\n", 1493243ca327SMatthew Dillon errno, strerror(errno)); 1494243ca327SMatthew Dillon exit(1); 1495243ca327SMatthew Dillon } 149617dd83bcSMatthew Dillon if (pad) { 149717dd83bcSMatthew Dillon bzero(zbuf, pad); 149817dd83bcSMatthew Dillon if (write(fdout, zbuf, pad) != pad) { 149917dd83bcSMatthew Dillon fprintf(stderr, "write_mrecord: error %d (%s)\n", 150017dd83bcSMatthew Dillon errno, strerror(errno)); 150117dd83bcSMatthew Dillon exit(1); 150217dd83bcSMatthew Dillon } 150317dd83bcSMatthew Dillon } 1504243ca327SMatthew Dillon } 1505243ca327SMatthew Dillon 1506243ca327SMatthew Dillon /* 150734ebae70SMatthew Dillon * Generate a mirroring header with the pfs information of the 150834ebae70SMatthew Dillon * originating filesytem. 150934ebae70SMatthew Dillon */ 151034ebae70SMatthew Dillon static void 1511e7f926a5SMatthew Dillon generate_mrec_header(int fd, int pfs_id, 1512e7f926a5SMatthew Dillon union hammer_ioc_mrecord_any *mrec_tmp) 151334ebae70SMatthew Dillon { 151434ebae70SMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 151534ebae70SMatthew Dillon 151634ebae70SMatthew Dillon bzero(&pfs, sizeof(pfs)); 1517e7f926a5SMatthew Dillon bzero(mrec_tmp, sizeof(*mrec_tmp)); 1518d4e5b69bSMatthew Dillon pfs.pfs_id = pfs_id; 1519e7f926a5SMatthew Dillon pfs.ondisk = &mrec_tmp->pfs.pfsd; 1520e7f926a5SMatthew Dillon pfs.bytes = sizeof(mrec_tmp->pfs.pfsd); 152134ebae70SMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 15229dc76cb1SMatthew Dillon fprintf(stderr, "Mirror-read: not a HAMMER fs/pseudofs!\n"); 152334ebae70SMatthew Dillon exit(1); 152434ebae70SMatthew Dillon } 152534ebae70SMatthew Dillon if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) { 15269dc76cb1SMatthew Dillon fprintf(stderr, "Mirror-read: HAMMER pfs version mismatch!\n"); 152734ebae70SMatthew Dillon exit(1); 152834ebae70SMatthew Dillon } 1529e7f926a5SMatthew Dillon mrec_tmp->pfs.version = pfs.version; 153034ebae70SMatthew Dillon } 153134ebae70SMatthew Dillon 153234ebae70SMatthew Dillon /* 153334ebae70SMatthew Dillon * Validate the pfs information from the originating filesystem 153434ebae70SMatthew Dillon * against the target filesystem. shared_uuid must match. 153548eadef9SMatthew Dillon * 153648eadef9SMatthew Dillon * return -1 if we got a TERM record 153734ebae70SMatthew Dillon */ 153848eadef9SMatthew Dillon static int 1539d4e5b69bSMatthew Dillon validate_mrec_header(int fd, int fdin, int is_target, int pfs_id, 154048eadef9SMatthew Dillon struct hammer_ioc_mrecord_head *pickup, 154134ebae70SMatthew Dillon hammer_tid_t *tid_begp, hammer_tid_t *tid_endp) 154234ebae70SMatthew Dillon { 154334ebae70SMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 154434ebae70SMatthew Dillon struct hammer_pseudofs_data pfsd; 154517dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 1546243ca327SMatthew Dillon int error; 154734ebae70SMatthew Dillon 154834ebae70SMatthew Dillon /* 154934ebae70SMatthew Dillon * Get the PFSD info from the target filesystem. 155034ebae70SMatthew Dillon */ 155134ebae70SMatthew Dillon bzero(&pfs, sizeof(pfs)); 155234ebae70SMatthew Dillon bzero(&pfsd, sizeof(pfsd)); 1553d4e5b69bSMatthew Dillon pfs.pfs_id = pfs_id; 155434ebae70SMatthew Dillon pfs.ondisk = &pfsd; 155534ebae70SMatthew Dillon pfs.bytes = sizeof(pfsd); 155634ebae70SMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 155734ebae70SMatthew Dillon fprintf(stderr, "mirror-write: not a HAMMER fs/pseudofs!\n"); 155834ebae70SMatthew Dillon exit(1); 155934ebae70SMatthew Dillon } 156034ebae70SMatthew Dillon if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) { 156134ebae70SMatthew Dillon fprintf(stderr, "mirror-write: HAMMER pfs version mismatch!\n"); 156234ebae70SMatthew Dillon exit(1); 156334ebae70SMatthew Dillon } 156434ebae70SMatthew Dillon 156548eadef9SMatthew Dillon mrec = read_mrecord(fdin, &error, pickup); 1566243ca327SMatthew Dillon if (mrec == NULL) { 1567243ca327SMatthew Dillon if (error == 0) 1568243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: short read\n"); 156934ebae70SMatthew Dillon exit(1); 157034ebae70SMatthew Dillon } 157148eadef9SMatthew Dillon if (mrec->head.type == HAMMER_MREC_TYPE_TERM) { 157248eadef9SMatthew Dillon free(mrec); 157348eadef9SMatthew Dillon return(-1); 157448eadef9SMatthew Dillon } 157548eadef9SMatthew Dillon 157617dd83bcSMatthew Dillon if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) { 1577243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: did not get expected " 1578243ca327SMatthew Dillon "PFSD record type\n"); 157934ebae70SMatthew Dillon exit(1); 158034ebae70SMatthew Dillon } 158117dd83bcSMatthew Dillon if (mrec->head.rec_size != sizeof(mrec->pfs)) { 1582243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: unexpected payload " 1583243ca327SMatthew Dillon "size\n"); 158434ebae70SMatthew Dillon exit(1); 158534ebae70SMatthew Dillon } 158617dd83bcSMatthew Dillon if (mrec->pfs.version != pfs.version) { 1587243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: Version mismatch\n"); 158834ebae70SMatthew Dillon exit(1); 158934ebae70SMatthew Dillon } 159034ebae70SMatthew Dillon 159134ebae70SMatthew Dillon /* 159234ebae70SMatthew Dillon * Whew. Ok, is the read PFS info compatible with the target? 159334ebae70SMatthew Dillon */ 159417dd83bcSMatthew Dillon if (bcmp(&mrec->pfs.pfsd.shared_uuid, &pfsd.shared_uuid, 159517dd83bcSMatthew Dillon sizeof(pfsd.shared_uuid)) != 0) { 159617dd83bcSMatthew Dillon fprintf(stderr, 159717dd83bcSMatthew Dillon "mirror-write: source and target have " 1598f265b84fSSascha Wildner "different shared-uuid's!\n"); 159934ebae70SMatthew Dillon exit(1); 160034ebae70SMatthew Dillon } 1601d4e5b69bSMatthew Dillon if (is_target && 1602d4e5b69bSMatthew Dillon (pfsd.mirror_flags & HAMMER_PFSD_SLAVE) == 0) { 160334ebae70SMatthew Dillon fprintf(stderr, "mirror-write: target must be in slave mode\n"); 160434ebae70SMatthew Dillon exit(1); 160534ebae70SMatthew Dillon } 1606d4e5b69bSMatthew Dillon if (tid_begp) 160717dd83bcSMatthew Dillon *tid_begp = mrec->pfs.pfsd.sync_beg_tid; 1608d4e5b69bSMatthew Dillon if (tid_endp) 160917dd83bcSMatthew Dillon *tid_endp = mrec->pfs.pfsd.sync_end_tid; 1610243ca327SMatthew Dillon free(mrec); 161148eadef9SMatthew Dillon return(0); 161234ebae70SMatthew Dillon } 161334ebae70SMatthew Dillon 161434ebae70SMatthew Dillon static void 1615d4e5b69bSMatthew Dillon update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id) 161634ebae70SMatthew Dillon { 1617243ca327SMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 1618243ca327SMatthew Dillon struct hammer_pseudofs_data pfsd; 161934ebae70SMatthew Dillon 1620243ca327SMatthew Dillon bzero(&pfs, sizeof(pfs)); 1621243ca327SMatthew Dillon bzero(&pfsd, sizeof(pfsd)); 1622d4e5b69bSMatthew Dillon pfs.pfs_id = pfs_id; 1623243ca327SMatthew Dillon pfs.ondisk = &pfsd; 1624243ca327SMatthew Dillon pfs.bytes = sizeof(pfsd); 1625243ca327SMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 1626243ca327SMatthew Dillon perror("update_pfs_snapshot (read)"); 1627243ca327SMatthew Dillon exit(1); 162834ebae70SMatthew Dillon } 16299c67b4d2SMatthew Dillon if (pfsd.sync_end_tid != snapshot_tid) { 1630ddc8e722SMatthew Dillon pfsd.sync_end_tid = snapshot_tid; 1631243ca327SMatthew Dillon if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) != 0) { 1632243ca327SMatthew Dillon perror("update_pfs_snapshot (rewrite)"); 1633243ca327SMatthew Dillon exit(1); 163434ebae70SMatthew Dillon } 163548eadef9SMatthew Dillon if (VerboseOpt >= 2) { 16369dc76cb1SMatthew Dillon fprintf(stderr, 1637ced4470dSMatthew Dillon "Mirror-write: Completed, updated snapshot " 1638a276dc6bSMatthew Dillon "to %016jx\n", 1639a276dc6bSMatthew Dillon (uintmax_t)snapshot_tid); 1640ced4470dSMatthew Dillon fflush(stderr); 1641243ca327SMatthew Dillon } 16429c67b4d2SMatthew Dillon } 164348eadef9SMatthew Dillon } 1644243ca327SMatthew Dillon 164548eadef9SMatthew Dillon /* 164648eadef9SMatthew Dillon * Bandwidth-limited write in chunks 164748eadef9SMatthew Dillon */ 164848eadef9SMatthew Dillon static 164948eadef9SMatthew Dillon ssize_t 165048eadef9SMatthew Dillon writebw(int fd, const void *buf, size_t nbytes, 165148eadef9SMatthew Dillon u_int64_t *bwcount, struct timeval *tv1) 165248eadef9SMatthew Dillon { 165348eadef9SMatthew Dillon struct timeval tv2; 165448eadef9SMatthew Dillon size_t n; 165548eadef9SMatthew Dillon ssize_t r; 165648eadef9SMatthew Dillon ssize_t a; 165748eadef9SMatthew Dillon int usec; 165848eadef9SMatthew Dillon 165948eadef9SMatthew Dillon a = 0; 166048eadef9SMatthew Dillon r = 0; 166148eadef9SMatthew Dillon while (nbytes) { 166248eadef9SMatthew Dillon if (*bwcount + nbytes > BandwidthOpt) 166348eadef9SMatthew Dillon n = BandwidthOpt - *bwcount; 166448eadef9SMatthew Dillon else 166548eadef9SMatthew Dillon n = nbytes; 166648eadef9SMatthew Dillon if (n) 166748eadef9SMatthew Dillon r = write(fd, buf, n); 166848eadef9SMatthew Dillon if (r >= 0) { 166948eadef9SMatthew Dillon a += r; 167048eadef9SMatthew Dillon nbytes -= r; 167148eadef9SMatthew Dillon buf = (const char *)buf + r; 167248eadef9SMatthew Dillon } 167348eadef9SMatthew Dillon if ((size_t)r != n) 167448eadef9SMatthew Dillon break; 167548eadef9SMatthew Dillon *bwcount += n; 167648eadef9SMatthew Dillon if (*bwcount >= BandwidthOpt) { 167748eadef9SMatthew Dillon gettimeofday(&tv2, NULL); 167848eadef9SMatthew Dillon usec = (int)(tv2.tv_sec - tv1->tv_sec) * 1000000 + 167948eadef9SMatthew Dillon (int)(tv2.tv_usec - tv1->tv_usec); 168048eadef9SMatthew Dillon if (usec >= 0 && usec < 1000000) 168148eadef9SMatthew Dillon usleep(1000000 - usec); 168248eadef9SMatthew Dillon gettimeofday(tv1, NULL); 168348eadef9SMatthew Dillon *bwcount -= BandwidthOpt; 168448eadef9SMatthew Dillon } 168548eadef9SMatthew Dillon } 168648eadef9SMatthew Dillon return(a ? a : r); 168748eadef9SMatthew Dillon } 168834ebae70SMatthew Dillon 168901a72c9fSMatthew Dillon /* 169001a72c9fSMatthew Dillon * Get a yes or no answer from the terminal. The program may be run as 169101a72c9fSMatthew Dillon * part of a two-way pipe so we cannot use stdin for this operation. 169201a72c9fSMatthew Dillon */ 169301a72c9fSMatthew Dillon static int 169401a72c9fSMatthew Dillon getyn(void) 169501a72c9fSMatthew Dillon { 169601a72c9fSMatthew Dillon char buf[256]; 169701a72c9fSMatthew Dillon FILE *fp; 169801a72c9fSMatthew Dillon int result; 169901a72c9fSMatthew Dillon 170001a72c9fSMatthew Dillon fp = fopen("/dev/tty", "r"); 170101a72c9fSMatthew Dillon if (fp == NULL) { 170201a72c9fSMatthew Dillon fprintf(stderr, "No terminal for response\n"); 170301a72c9fSMatthew Dillon return(-1); 170401a72c9fSMatthew Dillon } 170501a72c9fSMatthew Dillon result = -1; 170601a72c9fSMatthew Dillon while (fgets(buf, sizeof(buf), fp) != NULL) { 170701a72c9fSMatthew Dillon if (buf[0] == 'y' || buf[0] == 'Y') { 170801a72c9fSMatthew Dillon result = 1; 170901a72c9fSMatthew Dillon break; 171001a72c9fSMatthew Dillon } 171101a72c9fSMatthew Dillon if (buf[0] == 'n' || buf[0] == 'N') { 171201a72c9fSMatthew Dillon result = 0; 171301a72c9fSMatthew Dillon break; 171401a72c9fSMatthew Dillon } 171501a72c9fSMatthew Dillon fprintf(stderr, "Response not understood\n"); 171601a72c9fSMatthew Dillon break; 171701a72c9fSMatthew Dillon } 171801a72c9fSMatthew Dillon fclose(fp); 171901a72c9fSMatthew Dillon return(result); 172001a72c9fSMatthew Dillon } 172101a72c9fSMatthew Dillon 1722a7fbbf91SMatthew Dillon static void 1723a7fbbf91SMatthew Dillon mirror_usage(int code) 1724a7fbbf91SMatthew Dillon { 1725a7fbbf91SMatthew Dillon fprintf(stderr, 172634bb69d8SThomas Nikolajsen "hammer mirror-read <filesystem> [begin-tid]\n" 172734bb69d8SThomas Nikolajsen "hammer mirror-read-stream <filesystem> [begin-tid]\n" 1728a7fbbf91SMatthew Dillon "hammer mirror-write <filesystem>\n" 17299f1b0121SAntonio Huete Jimenez "hammer mirror-dump [header]\n" 173034bb69d8SThomas Nikolajsen "hammer mirror-copy [[user@]host:]<filesystem>" 173134bb69d8SThomas Nikolajsen " [[user@]host:]<filesystem>\n" 173234bb69d8SThomas Nikolajsen "hammer mirror-stream [[user@]host:]<filesystem>" 173334bb69d8SThomas Nikolajsen " [[user@]host:]<filesystem>\n" 1734a7fbbf91SMatthew Dillon ); 1735a7fbbf91SMatthew Dillon exit(code); 1736a7fbbf91SMatthew Dillon } 173701a72c9fSMatthew Dillon 1738