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; 4546137e17STomohiro Kusumi uint64_t bytes; 46ced4470dSMatthew Dillon } *histogram_t; 47ced4470dSMatthew Dillon 488679ed64STomohiro Kusumi const char *ScoreBoardFile; 498679ed64STomohiro Kusumi const char *RestrictTarget; 508679ed64STomohiro Kusumi 51a7fbbf91SMatthew Dillon static int read_mrecords(int fd, char *buf, u_int size, 5217dd83bcSMatthew Dillon hammer_ioc_mrecord_head_t pickup); 53e7f926a5SMatthew Dillon static int generate_histogram(int fd, const char *filesystem, 54ced4470dSMatthew Dillon histogram_t *histogram_ary, 5539e88285SMatthew Dillon struct hammer_ioc_mirror_rw *mirror_base, 5639e88285SMatthew Dillon int *repeatp); 5717dd83bcSMatthew Dillon static hammer_ioc_mrecord_any_t read_mrecord(int fdin, int *errorp, 5817dd83bcSMatthew Dillon hammer_ioc_mrecord_head_t pickup); 5946137e17STomohiro Kusumi static void write_mrecord(int fdout, uint32_t type, 6017dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec, int bytes); 61e7f926a5SMatthew Dillon static void generate_mrec_header(int fd, int pfs_id, 62e7f926a5SMatthew Dillon union hammer_ioc_mrecord_any *mrec_tmp); 6348eadef9SMatthew Dillon static int validate_mrec_header(int fd, int fdin, int is_target, int pfs_id, 6448eadef9SMatthew Dillon struct hammer_ioc_mrecord_head *pickup, 6534ebae70SMatthew Dillon hammer_tid_t *tid_begp, hammer_tid_t *tid_endp); 66d4e5b69bSMatthew Dillon static void update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id); 6748eadef9SMatthew Dillon static ssize_t writebw(int fd, const void *buf, size_t nbytes, 6846137e17STomohiro Kusumi uint64_t *bwcount, struct timeval *tv1); 69778c2d2cSTomohiro Kusumi static int getyntty(void); 708679ed64STomohiro Kusumi static void score_printf(size_t i, size_t w, const char *ctl, ...); 718679ed64STomohiro Kusumi static void hammer_check_restrict(const char *filesystem); 72a7fbbf91SMatthew Dillon static void mirror_usage(int code); 73a7fbbf91SMatthew Dillon 7417dd83bcSMatthew Dillon /* 7517dd83bcSMatthew Dillon * Generate a mirroring data stream from the specific source over the 7617dd83bcSMatthew Dillon * entire key range, but restricted to the specified transaction range. 7717dd83bcSMatthew Dillon * 7817dd83bcSMatthew Dillon * The HAMMER VFS does most of the work, we add a few new mrecord 7917dd83bcSMatthew Dillon * types to negotiate the TID ranges and verify that the entire 8017dd83bcSMatthew Dillon * stream made it to the destination. 8139e88285SMatthew Dillon * 8239e88285SMatthew Dillon * streaming will be 0 for mirror-read, 1 for mirror-stream. The code will 8339e88285SMatthew Dillon * set up a fake value of -1 when running the histogram for mirror-read. 8417dd83bcSMatthew Dillon */ 85a7fbbf91SMatthew Dillon void 8648eadef9SMatthew Dillon hammer_cmd_mirror_read(char **av, int ac, int streaming) 87a7fbbf91SMatthew Dillon { 88a7fbbf91SMatthew Dillon struct hammer_ioc_mirror_rw mirror; 89d4e5b69bSMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 9017dd83bcSMatthew Dillon union hammer_ioc_mrecord_any mrec_tmp; 9148eadef9SMatthew Dillon struct hammer_ioc_mrecord_head pickup; 9217dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 93243ca327SMatthew Dillon hammer_tid_t sync_tid; 94ced4470dSMatthew Dillon histogram_t histogram_ary; 95578f0d56STomohiro Kusumi const char *filesystem; 96a7fbbf91SMatthew Dillon char *buf = malloc(SERIALBUF_SIZE); 97243ca327SMatthew Dillon int interrupted = 0; 98243ca327SMatthew Dillon int error; 99a7fbbf91SMatthew Dillon int fd; 100243ca327SMatthew Dillon int n; 10148eadef9SMatthew Dillon int didwork; 102e7f926a5SMatthew Dillon int histogram; 103ced4470dSMatthew Dillon int histindex; 104ced4470dSMatthew Dillon int histmax; 10539e88285SMatthew Dillon int repeat = 0; 10639e88285SMatthew Dillon int sameline; 10748eadef9SMatthew Dillon int64_t total_bytes; 108243ca327SMatthew Dillon time_t base_t = time(NULL); 10948eadef9SMatthew Dillon struct timeval bwtv; 11046137e17STomohiro Kusumi uint64_t bwcount; 11146137e17STomohiro Kusumi uint64_t estbytes; 112a7fbbf91SMatthew Dillon 11388cdee70STomohiro Kusumi if (ac == 0 || ac > 2) { 114a7fbbf91SMatthew Dillon mirror_usage(1); 11588cdee70STomohiro Kusumi /* not reached */ 11688cdee70STomohiro Kusumi } 117a7fbbf91SMatthew Dillon filesystem = av[0]; 11869f5a58cSMatthew Dillon hammer_check_restrict(filesystem); 119a7fbbf91SMatthew Dillon 12048eadef9SMatthew Dillon pickup.signature = 0; 12148eadef9SMatthew Dillon pickup.type = 0; 122ced4470dSMatthew Dillon histogram = 0; 123ced4470dSMatthew Dillon histindex = 0; 124ced4470dSMatthew Dillon histmax = 0; 125e7f926a5SMatthew Dillon histogram_ary = NULL; 12639e88285SMatthew Dillon sameline = 0; 12748eadef9SMatthew Dillon 12848eadef9SMatthew Dillon again: 129a7fbbf91SMatthew Dillon bzero(&mirror, sizeof(mirror)); 130a7fbbf91SMatthew Dillon hammer_key_beg_init(&mirror.key_beg); 131a7fbbf91SMatthew Dillon hammer_key_end_init(&mirror.key_end); 132a7fbbf91SMatthew Dillon 133d4e5b69bSMatthew Dillon fd = getpfs(&pfs, filesystem); 134a7fbbf91SMatthew Dillon 135269cdd19SMatthew Dillon if (streaming >= 0) 136269cdd19SMatthew Dillon score_printf(LINE1, "Running"); 137269cdd19SMatthew Dillon 13839e88285SMatthew Dillon if (streaming >= 0 && VerboseOpt && VerboseOpt < 2) { 13939e88285SMatthew Dillon fprintf(stderr, "%cRunning \b\b", (sameline ? '\r' : '\n')); 14048eadef9SMatthew Dillon fflush(stderr); 14139e88285SMatthew Dillon sameline = 1; 14248eadef9SMatthew Dillon } 14339e88285SMatthew Dillon sameline = 1; 14448eadef9SMatthew Dillon total_bytes = 0; 14548eadef9SMatthew Dillon gettimeofday(&bwtv, NULL); 14648eadef9SMatthew Dillon bwcount = 0; 14748eadef9SMatthew Dillon 148243ca327SMatthew Dillon /* 149e7f926a5SMatthew Dillon * Send initial header for the purpose of determining the 150e7f926a5SMatthew Dillon * shared-uuid. 15101a72c9fSMatthew Dillon */ 152e7f926a5SMatthew Dillon generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp); 153e7f926a5SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_PFSD, 154e7f926a5SMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.pfs)); 15501a72c9fSMatthew Dillon 15601a72c9fSMatthew Dillon /* 157d4e5b69bSMatthew Dillon * In 2-way mode the target will send us a PFS info packet 158d4e5b69bSMatthew Dillon * first. Use the target's current snapshot TID as our default 159d4e5b69bSMatthew Dillon * begin TID. 160243ca327SMatthew Dillon */ 16148eadef9SMatthew Dillon if (TwoWayPipeOpt) { 16239e88285SMatthew Dillon mirror.tid_beg = 0; 16348eadef9SMatthew Dillon n = validate_mrec_header(fd, 0, 0, pfs.pfs_id, &pickup, 164d4e5b69bSMatthew Dillon NULL, &mirror.tid_beg); 16548eadef9SMatthew Dillon if (n < 0) { /* got TERM record */ 16648eadef9SMatthew Dillon relpfs(fd, &pfs); 167764d0c8cSTomohiro Kusumi free(buf); 168764d0c8cSTomohiro Kusumi free(histogram_ary); 16948eadef9SMatthew Dillon return; 17048eadef9SMatthew Dillon } 17148eadef9SMatthew Dillon ++mirror.tid_beg; 17239e88285SMatthew Dillon } else if (streaming && histogram) { 17339e88285SMatthew Dillon mirror.tid_beg = histogram_ary[histindex].tid + 1; 17439e88285SMatthew Dillon } else { 17539e88285SMatthew Dillon mirror.tid_beg = 0; 17648eadef9SMatthew Dillon } 177d4e5b69bSMatthew Dillon 178d4e5b69bSMatthew Dillon /* 179d4e5b69bSMatthew Dillon * Write out the PFS header, tid_beg will be updated if our PFS 180d4e5b69bSMatthew Dillon * has a larger begin sync. tid_end is set to the latest source 181d4e5b69bSMatthew Dillon * TID whos flush cycle has completed. 182d4e5b69bSMatthew Dillon */ 183e7f926a5SMatthew Dillon generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp); 184e7f926a5SMatthew Dillon if (mirror.tid_beg < mrec_tmp.pfs.pfsd.sync_beg_tid) 185e7f926a5SMatthew Dillon mirror.tid_beg = mrec_tmp.pfs.pfsd.sync_beg_tid; 186e7f926a5SMatthew Dillon mirror.tid_end = mrec_tmp.pfs.pfsd.sync_end_tid; 187e7f926a5SMatthew Dillon mirror.ubuf = buf; 188e7f926a5SMatthew Dillon mirror.size = SERIALBUF_SIZE; 189e7f926a5SMatthew Dillon mirror.pfs_id = pfs.pfs_id; 190e7f926a5SMatthew Dillon mirror.shared_uuid = pfs.ondisk->shared_uuid; 191d4e5b69bSMatthew Dillon 192d4e5b69bSMatthew Dillon /* 193e7f926a5SMatthew Dillon * XXX If the histogram is exhausted and the TID delta is large 194e7f926a5SMatthew Dillon * the stream might have been offline for a while and is 195e7f926a5SMatthew Dillon * now picking it up again. Do another histogram. 196d4e5b69bSMatthew Dillon */ 197e7f926a5SMatthew Dillon #if 0 198f254e677STomohiro Kusumi if (streaming && histogram && histindex == histend) { 199e7f926a5SMatthew Dillon if (mirror.tid_end - mirror.tid_beg > BULK_MINIMUM) 200ced4470dSMatthew Dillon histogram = 0; 201f254e677STomohiro Kusumi } 202e7f926a5SMatthew Dillon #endif 20334ebae70SMatthew Dillon 204e7f926a5SMatthew Dillon /* 205e7f926a5SMatthew Dillon * Initial bulk startup control, try to do some incremental 206e7f926a5SMatthew Dillon * mirroring in order to allow the stream to be killed and 207e7f926a5SMatthew Dillon * restarted without having to start over. 208e7f926a5SMatthew Dillon */ 209ced4470dSMatthew Dillon if (histogram == 0 && BulkOpt == 0) { 21039e88285SMatthew Dillon if (VerboseOpt && repeat == 0) { 211e7f926a5SMatthew Dillon fprintf(stderr, "\n"); 21239e88285SMatthew Dillon sameline = 0; 21339e88285SMatthew Dillon } 214ced4470dSMatthew Dillon histmax = generate_histogram(fd, filesystem, 21539e88285SMatthew Dillon &histogram_ary, &mirror, 21639e88285SMatthew Dillon &repeat); 217ced4470dSMatthew Dillon histindex = 0; 218ced4470dSMatthew Dillon histogram = 1; 21939e88285SMatthew Dillon 22039e88285SMatthew Dillon /* 22139e88285SMatthew Dillon * Just stream the histogram, then stop 22239e88285SMatthew Dillon */ 22339e88285SMatthew Dillon if (streaming == 0) 22439e88285SMatthew Dillon streaming = -1; 225e7f926a5SMatthew Dillon } 226e7f926a5SMatthew Dillon 22739e88285SMatthew Dillon if (streaming && histogram) { 228ced4470dSMatthew Dillon ++histindex; 229ced4470dSMatthew Dillon mirror.tid_end = histogram_ary[histindex].tid; 230ced4470dSMatthew Dillon estbytes = histogram_ary[histindex-1].bytes; 231e7f926a5SMatthew Dillon mrec_tmp.pfs.pfsd.sync_end_tid = mirror.tid_end; 232ced4470dSMatthew Dillon } else { 233ced4470dSMatthew Dillon estbytes = 0; 234e7f926a5SMatthew Dillon } 235e7f926a5SMatthew Dillon 236e7f926a5SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_PFSD, 237e7f926a5SMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.pfs)); 238e7f926a5SMatthew Dillon 239e7f926a5SMatthew Dillon /* 240e7f926a5SMatthew Dillon * A cycle file overrides the beginning TID only if we are 24139e88285SMatthew Dillon * not operating in two-way or histogram mode. 242e7f926a5SMatthew Dillon */ 24352e2f1b5STomohiro Kusumi if (TwoWayPipeOpt == 0 && histogram == 0) 244e7f926a5SMatthew Dillon hammer_get_cycle(&mirror.key_beg, &mirror.tid_beg); 245e7f926a5SMatthew Dillon 246e7f926a5SMatthew Dillon /* 247e7f926a5SMatthew Dillon * An additional argument overrides the beginning TID regardless 248e7f926a5SMatthew Dillon * of what mode we are in. This is not recommending if operating 249e7f926a5SMatthew Dillon * in two-way mode. 250e7f926a5SMatthew Dillon */ 25117dd83bcSMatthew Dillon if (ac == 2) 25217dd83bcSMatthew Dillon mirror.tid_beg = strtoull(av[1], NULL, 0); 25317dd83bcSMatthew Dillon 25448eadef9SMatthew Dillon if (streaming == 0 || VerboseOpt >= 2) { 25548eadef9SMatthew Dillon fprintf(stderr, 256ced4470dSMatthew Dillon "Mirror-read: Mirror %016jx to %016jx", 257a276dc6bSMatthew Dillon (uintmax_t)mirror.tid_beg, (uintmax_t)mirror.tid_end); 258ced4470dSMatthew Dillon if (histogram) 259ced4470dSMatthew Dillon fprintf(stderr, " (bulk= %ju)", (uintmax_t)estbytes); 2603d7b2393SMatthew Dillon fprintf(stderr, "\n"); 2613d7b2393SMatthew Dillon fflush(stderr); 26248eadef9SMatthew Dillon } 263f254e677STomohiro Kusumi if (mirror.key_beg.obj_id != (int64_t)HAMMER_MIN_OBJID) { 264a276dc6bSMatthew Dillon fprintf(stderr, "Mirror-read: Resuming at object %016jx\n", 265a276dc6bSMatthew Dillon (uintmax_t)mirror.key_beg.obj_id); 266f254e677STomohiro Kusumi } 267243ca327SMatthew Dillon 268243ca327SMatthew Dillon /* 2699dc76cb1SMatthew Dillon * Nothing to do if begin equals end. 2709dc76cb1SMatthew Dillon */ 27148eadef9SMatthew Dillon if (mirror.tid_beg >= mirror.tid_end) { 27248eadef9SMatthew Dillon if (streaming == 0 || VerboseOpt >= 2) 27348eadef9SMatthew Dillon fprintf(stderr, "Mirror-read: No work to do\n"); 27439e88285SMatthew Dillon sleep(DelayOpt); 27548eadef9SMatthew Dillon didwork = 0; 27639e88285SMatthew Dillon histogram = 0; 2779dc76cb1SMatthew Dillon goto done; 2789dc76cb1SMatthew Dillon } 27948eadef9SMatthew Dillon didwork = 1; 2809dc76cb1SMatthew Dillon 2819dc76cb1SMatthew Dillon /* 282243ca327SMatthew Dillon * Write out bulk records 283243ca327SMatthew Dillon */ 284a7fbbf91SMatthew Dillon mirror.ubuf = buf; 285a7fbbf91SMatthew Dillon mirror.size = SERIALBUF_SIZE; 286a7fbbf91SMatthew Dillon 287a7fbbf91SMatthew Dillon do { 288a7fbbf91SMatthew Dillon mirror.count = 0; 289d4e5b69bSMatthew Dillon mirror.pfs_id = pfs.pfs_id; 290d4e5b69bSMatthew Dillon mirror.shared_uuid = pfs.ondisk->shared_uuid; 291a7fbbf91SMatthew Dillon if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) { 292269cdd19SMatthew Dillon score_printf(LINE3, "Mirror-read %s failed: %s", 293269cdd19SMatthew Dillon filesystem, strerror(errno)); 29402318f07STomohiro Kusumi err(1, "Mirror-read %s failed", filesystem); 295052fd72bSTomohiro Kusumi /* not reached */ 296a7fbbf91SMatthew Dillon } 2979c67b4d2SMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) { 298269cdd19SMatthew Dillon score_printf(LINE3, "Mirror-read %s fatal error %d", 299269cdd19SMatthew Dillon filesystem, mirror.head.error); 30002318f07STomohiro Kusumi errx(1, "Mirror-read %s fatal error %d", 3019c67b4d2SMatthew Dillon filesystem, mirror.head.error); 302052fd72bSTomohiro Kusumi /* not reached */ 3039c67b4d2SMatthew Dillon } 304243ca327SMatthew Dillon if (mirror.count) { 305f254e677STomohiro Kusumi if (BandwidthOpt) { 30648eadef9SMatthew Dillon n = writebw(1, mirror.ubuf, mirror.count, 30748eadef9SMatthew Dillon &bwcount, &bwtv); 308f254e677STomohiro Kusumi } else { 309243ca327SMatthew Dillon n = write(1, mirror.ubuf, mirror.count); 310f254e677STomohiro Kusumi } 311243ca327SMatthew Dillon if (n != mirror.count) { 312269cdd19SMatthew Dillon score_printf(LINE3, 313269cdd19SMatthew Dillon "Mirror-read %s failed: " 314269cdd19SMatthew Dillon "short write", 315269cdd19SMatthew Dillon filesystem); 31602318f07STomohiro Kusumi errx(1, "Mirror-read %s failed: short write", 317243ca327SMatthew Dillon filesystem); 318052fd72bSTomohiro Kusumi /* not reached */ 319243ca327SMatthew Dillon } 320a7fbbf91SMatthew Dillon } 32148eadef9SMatthew Dillon total_bytes += mirror.count; 32248eadef9SMatthew Dillon if (streaming && VerboseOpt) { 323e7f926a5SMatthew Dillon fprintf(stderr, 32439e88285SMatthew Dillon "\rscan obj=%016jx tids=%016jx:%016jx %11jd", 325a276dc6bSMatthew Dillon (uintmax_t)mirror.key_cur.obj_id, 326a276dc6bSMatthew Dillon (uintmax_t)mirror.tid_beg, 327a276dc6bSMatthew Dillon (uintmax_t)mirror.tid_end, 328a276dc6bSMatthew Dillon (intmax_t)total_bytes); 32948eadef9SMatthew Dillon fflush(stderr); 33039e88285SMatthew Dillon sameline = 0; 331269cdd19SMatthew Dillon } else if (streaming) { 332269cdd19SMatthew Dillon score_printf(LINE2, 333269cdd19SMatthew Dillon "obj=%016jx tids=%016jx:%016jx %11jd", 334269cdd19SMatthew Dillon (uintmax_t)mirror.key_cur.obj_id, 335269cdd19SMatthew Dillon (uintmax_t)mirror.tid_beg, 336269cdd19SMatthew Dillon (uintmax_t)mirror.tid_end, 337269cdd19SMatthew Dillon (intmax_t)total_bytes); 33848eadef9SMatthew Dillon } 339a7fbbf91SMatthew Dillon mirror.key_beg = mirror.key_cur; 340e7f926a5SMatthew Dillon 341e7f926a5SMatthew Dillon /* 342e7f926a5SMatthew Dillon * Deal with time limit option 343e7f926a5SMatthew Dillon */ 344243ca327SMatthew Dillon if (TimeoutOpt && 345243ca327SMatthew Dillon (unsigned)(time(NULL) - base_t) > (unsigned)TimeoutOpt) { 346269cdd19SMatthew Dillon score_printf(LINE3, 347269cdd19SMatthew Dillon "Mirror-read %s interrupted by timer at" 348269cdd19SMatthew Dillon " %016jx", 349269cdd19SMatthew Dillon filesystem, 350269cdd19SMatthew Dillon (uintmax_t)mirror.key_cur.obj_id); 351243ca327SMatthew Dillon fprintf(stderr, 352243ca327SMatthew Dillon "Mirror-read %s interrupted by timer at" 353a276dc6bSMatthew Dillon " %016jx\n", 354243ca327SMatthew Dillon filesystem, 355a276dc6bSMatthew Dillon (uintmax_t)mirror.key_cur.obj_id); 356243ca327SMatthew Dillon interrupted = 1; 357243ca327SMatthew Dillon break; 358243ca327SMatthew Dillon } 359a7fbbf91SMatthew Dillon } while (mirror.count != 0); 360a7fbbf91SMatthew Dillon 36148eadef9SMatthew Dillon done: 36239e88285SMatthew Dillon if (streaming && VerboseOpt && sameline == 0) { 363ced4470dSMatthew Dillon fprintf(stderr, "\n"); 364ced4470dSMatthew Dillon fflush(stderr); 36539e88285SMatthew Dillon sameline = 1; 366ced4470dSMatthew Dillon } 367ced4470dSMatthew Dillon 368243ca327SMatthew Dillon /* 36948eadef9SMatthew Dillon * Write out the termination sync record - only if not interrupted 370243ca327SMatthew Dillon */ 37148eadef9SMatthew Dillon if (interrupted == 0) { 372f254e677STomohiro Kusumi if (didwork) { 37317dd83bcSMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_SYNC, 37417dd83bcSMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.sync)); 375f254e677STomohiro Kusumi } else { 37648eadef9SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_IDLE, 37748eadef9SMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.sync)); 37848eadef9SMatthew Dillon } 379f254e677STomohiro Kusumi } 38034ebae70SMatthew Dillon 381243ca327SMatthew Dillon /* 382243ca327SMatthew Dillon * If the -2 option was given (automatic when doing mirror-copy), 383243ca327SMatthew Dillon * a two-way pipe is assumed and we expect a response mrec from 384243ca327SMatthew Dillon * the target. 385243ca327SMatthew Dillon */ 386243ca327SMatthew Dillon if (TwoWayPipeOpt) { 38748eadef9SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 38817dd83bcSMatthew Dillon if (mrec == NULL || 38917dd83bcSMatthew Dillon mrec->head.type != HAMMER_MREC_TYPE_UPDATE || 390f254e677STomohiro Kusumi mrec->head.rec_size != sizeof(mrec->update)) { 39102318f07STomohiro Kusumi errx(1, "mirror_read: Did not get final " 39202318f07STomohiro Kusumi "acknowledgement packet from target"); 393052fd72bSTomohiro Kusumi /* not reached */ 394f254e677STomohiro Kusumi } 395243ca327SMatthew Dillon if (interrupted) { 396243ca327SMatthew Dillon if (CyclePath) { 397ced4470dSMatthew Dillon hammer_set_cycle(&mirror.key_cur, 398ced4470dSMatthew Dillon mirror.tid_beg); 39934bb69d8SThomas Nikolajsen fprintf(stderr, "Cyclefile %s updated for " 40034bb69d8SThomas Nikolajsen "continuation\n", CyclePath); 401243ca327SMatthew Dillon } 402243ca327SMatthew Dillon } else { 40317dd83bcSMatthew Dillon sync_tid = mrec->update.tid; 404243ca327SMatthew Dillon if (CyclePath) { 405243ca327SMatthew Dillon hammer_key_beg_init(&mirror.key_beg); 406243ca327SMatthew Dillon hammer_set_cycle(&mirror.key_beg, sync_tid); 407a276dc6bSMatthew Dillon fprintf(stderr, 408a276dc6bSMatthew Dillon "Cyclefile %s updated to 0x%016jx\n", 409a276dc6bSMatthew Dillon CyclePath, (uintmax_t)sync_tid); 410243ca327SMatthew Dillon } 411243ca327SMatthew Dillon } 412cc71ff00STomohiro Kusumi free(mrec); 413243ca327SMatthew Dillon } else if (CyclePath) { 414243ca327SMatthew Dillon /* NOTE! mirror.tid_beg cannot be updated */ 415243ca327SMatthew Dillon fprintf(stderr, "Warning: cycle file (-c option) cannot be " 416243ca327SMatthew Dillon "fully updated unless you use mirror-copy\n"); 417243ca327SMatthew Dillon hammer_set_cycle(&mirror.key_beg, mirror.tid_beg); 418243ca327SMatthew Dillon } 41948eadef9SMatthew Dillon if (streaming && interrupted == 0) { 42048eadef9SMatthew Dillon time_t t1 = time(NULL); 42148eadef9SMatthew Dillon time_t t2; 42248eadef9SMatthew Dillon 423e7f926a5SMatthew Dillon /* 42439e88285SMatthew Dillon * Try to break down large bulk transfers into smaller ones 42539e88285SMatthew Dillon * so it can sync the transaction id on the slave. This 42639e88285SMatthew Dillon * way if we get interrupted a restart doesn't have to 42739e88285SMatthew Dillon * start from scratch. 428e7f926a5SMatthew Dillon */ 429f254e677STomohiro Kusumi if (streaming && histogram) { 430ced4470dSMatthew Dillon if (histindex != histmax) { 43139e88285SMatthew Dillon if (VerboseOpt && VerboseOpt < 2 && 432f254e677STomohiro Kusumi streaming >= 0) { 433e7f926a5SMatthew Dillon fprintf(stderr, " (bulk incremental)"); 434f254e677STomohiro Kusumi } 43539e88285SMatthew Dillon relpfs(fd, &pfs); 436e7f926a5SMatthew Dillon goto again; 437e7f926a5SMatthew Dillon } 438f254e677STomohiro Kusumi } 439e7f926a5SMatthew Dillon 44039e88285SMatthew Dillon if (VerboseOpt && streaming >= 0) { 44148eadef9SMatthew Dillon fprintf(stderr, " W"); 44248eadef9SMatthew Dillon fflush(stderr); 443269cdd19SMatthew Dillon } else if (streaming >= 0) { 444269cdd19SMatthew Dillon score_printf(LINE1, "Waiting"); 44548eadef9SMatthew Dillon } 44648eadef9SMatthew Dillon pfs.ondisk->sync_end_tid = mirror.tid_end; 44739e88285SMatthew Dillon if (streaming < 0) { 44839e88285SMatthew Dillon /* 44939e88285SMatthew Dillon * Fake streaming mode when using a histogram to 45039e88285SMatthew Dillon * break up a mirror-read, do not wait on source. 45139e88285SMatthew Dillon */ 45239e88285SMatthew Dillon streaming = 0; 45339e88285SMatthew Dillon } else if (ioctl(fd, HAMMERIOC_WAI_PSEUDOFS, &pfs) < 0) { 454269cdd19SMatthew Dillon score_printf(LINE3, 455269cdd19SMatthew Dillon "Mirror-read %s: cannot stream: %s\n", 456269cdd19SMatthew Dillon filesystem, strerror(errno)); 457269cdd19SMatthew Dillon fprintf(stderr, 458269cdd19SMatthew Dillon "Mirror-read %s: cannot stream: %s\n", 45948eadef9SMatthew Dillon filesystem, strerror(errno)); 46048eadef9SMatthew Dillon } else { 46148eadef9SMatthew Dillon t2 = time(NULL) - t1; 46248eadef9SMatthew Dillon if (t2 >= 0 && t2 < DelayOpt) { 46348eadef9SMatthew Dillon if (VerboseOpt) { 46448eadef9SMatthew Dillon fprintf(stderr, "\bD"); 46548eadef9SMatthew Dillon fflush(stderr); 46648eadef9SMatthew Dillon } 46748eadef9SMatthew Dillon sleep(DelayOpt - t2); 46848eadef9SMatthew Dillon } 46948eadef9SMatthew Dillon if (VerboseOpt) { 47048eadef9SMatthew Dillon fprintf(stderr, "\b "); 47148eadef9SMatthew Dillon fflush(stderr); 47248eadef9SMatthew Dillon } 47348eadef9SMatthew Dillon relpfs(fd, &pfs); 47448eadef9SMatthew Dillon goto again; 47548eadef9SMatthew Dillon } 47648eadef9SMatthew Dillon } 47748eadef9SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_TERM, 47848eadef9SMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.sync)); 47948eadef9SMatthew Dillon relpfs(fd, &pfs); 480764d0c8cSTomohiro Kusumi free(buf); 481764d0c8cSTomohiro Kusumi free(histogram_ary); 482a7fbbf91SMatthew Dillon fprintf(stderr, "Mirror-read %s succeeded\n", filesystem); 483a7fbbf91SMatthew Dillon } 484a7fbbf91SMatthew Dillon 485e7f926a5SMatthew Dillon /* 4863d7b2393SMatthew Dillon * What we are trying to do here is figure out how much data is 4873d7b2393SMatthew Dillon * going to be sent for the TID range and to break the TID range 4883d7b2393SMatthew Dillon * down into reasonably-sized slices (from the point of view of 4893d7b2393SMatthew Dillon * data sent) so a lost connection can restart at a reasonable 4903d7b2393SMatthew Dillon * place and not all the way back at the beginning. 491ced4470dSMatthew Dillon * 492ced4470dSMatthew Dillon * An entry's TID serves as the end_tid for the prior entry 493ced4470dSMatthew Dillon * So we have to offset the calculation by 1 so that TID falls into 494ced4470dSMatthew Dillon * the previous entry when populating entries. 495ced4470dSMatthew Dillon * 496ced4470dSMatthew Dillon * Because the transaction id space is bursty we need a relatively 497ced4470dSMatthew Dillon * large number of buckets (like a million) to do a reasonable job 498ced4470dSMatthew Dillon * for things like an initial bulk mirrors on a very large filesystem. 499e7f926a5SMatthew Dillon */ 500ced4470dSMatthew Dillon #define HIST_COUNT (1024 * 1024) 5013d7b2393SMatthew Dillon 502*005a4da7STomohiro Kusumi static 503*005a4da7STomohiro Kusumi int 504e7f926a5SMatthew Dillon generate_histogram(int fd, const char *filesystem, 505ced4470dSMatthew Dillon histogram_t *histogram_ary, 50639e88285SMatthew Dillon struct hammer_ioc_mirror_rw *mirror_base, 50739e88285SMatthew Dillon int *repeatp) 508e7f926a5SMatthew Dillon { 509e7f926a5SMatthew Dillon struct hammer_ioc_mirror_rw mirror; 5103d7b2393SMatthew Dillon union hammer_ioc_mrecord_any *mrec; 511e7f926a5SMatthew Dillon hammer_tid_t tid_beg; 512e7f926a5SMatthew Dillon hammer_tid_t tid_end; 513ced4470dSMatthew Dillon hammer_tid_t tid; 514ced4470dSMatthew Dillon hammer_tid_t tidx; 51546137e17STomohiro Kusumi uint64_t *tid_bytes; 51646137e17STomohiro Kusumi uint64_t total; 51746137e17STomohiro Kusumi uint64_t accum; 518269cdd19SMatthew Dillon int chunkno; 519e7f926a5SMatthew Dillon int i; 5203d7b2393SMatthew Dillon int res; 5213d7b2393SMatthew Dillon int off; 5223d7b2393SMatthew Dillon int len; 523e7f926a5SMatthew Dillon 524e7f926a5SMatthew Dillon mirror = *mirror_base; 525e7f926a5SMatthew Dillon tid_beg = mirror.tid_beg; 526e7f926a5SMatthew Dillon tid_end = mirror.tid_end; 5273d7b2393SMatthew Dillon mirror.head.flags |= HAMMER_IOC_MIRROR_NODATA; 528e7f926a5SMatthew Dillon 529f254e677STomohiro Kusumi if (*histogram_ary == NULL) { 530ced4470dSMatthew Dillon *histogram_ary = malloc(sizeof(struct histogram) * 5313d7b2393SMatthew Dillon (HIST_COUNT + 2)); 532f254e677STomohiro Kusumi } 5333d7b2393SMatthew Dillon if (tid_beg >= tid_end) 534e7f926a5SMatthew Dillon return(0); 535e7f926a5SMatthew Dillon 536ced4470dSMatthew Dillon /* needs 2 extra */ 537ced4470dSMatthew Dillon tid_bytes = malloc(sizeof(*tid_bytes) * (HIST_COUNT + 2)); 5384839c61eSSascha Wildner bzero(tid_bytes, sizeof(*tid_bytes) * (HIST_COUNT + 2)); 539ced4470dSMatthew Dillon 54039e88285SMatthew Dillon if (*repeatp == 0) { 5413d7b2393SMatthew Dillon fprintf(stderr, "Prescan to break up bulk transfer"); 5423d7b2393SMatthew Dillon if (VerboseOpt > 1) 5433d7b2393SMatthew Dillon fprintf(stderr, " (%juMB chunks)", 5443d7b2393SMatthew Dillon (uintmax_t)(SplitupOpt / (1024 * 1024))); 5453d7b2393SMatthew Dillon fprintf(stderr, "\n"); 54639e88285SMatthew Dillon } 547e7f926a5SMatthew Dillon 548ced4470dSMatthew Dillon /* 549ced4470dSMatthew Dillon * Note: (tid_beg,tid_end), range is inclusive of both beg & end. 550ced4470dSMatthew Dillon * 551ced4470dSMatthew Dillon * Note: Estimates can be off when the mirror is way behind due 552ced4470dSMatthew Dillon * to skips. 553ced4470dSMatthew Dillon */ 5543d7b2393SMatthew Dillon total = 0; 5553d7b2393SMatthew Dillon accum = 0; 556269cdd19SMatthew Dillon chunkno = 0; 5573d7b2393SMatthew Dillon for (;;) { 5583d7b2393SMatthew Dillon mirror.count = 0; 559052fd72bSTomohiro Kusumi if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) { 56002318f07STomohiro Kusumi err(1, "Mirror-read %s failed", filesystem); 561052fd72bSTomohiro Kusumi /* not reached */ 562052fd72bSTomohiro Kusumi } 563052fd72bSTomohiro Kusumi if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) { 56402318f07STomohiro Kusumi errx(1, "Mirror-read %s fatal error %d", 565e7f926a5SMatthew Dillon filesystem, mirror.head.error); 566052fd72bSTomohiro Kusumi /* not reached */ 567052fd72bSTomohiro Kusumi } 5683d7b2393SMatthew Dillon for (off = 0; 5693d7b2393SMatthew Dillon off < mirror.count; 5709a620123STomohiro Kusumi off += HAMMER_HEAD_DOALIGN(mrec->head.rec_size)) { 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 == 619f254e677STomohiro Kusumi HAMMER_MREC_TYPE_REC_NODATA) { 620ced4470dSMatthew Dillon len += HAMMER_HEAD_DOALIGN( 621ced4470dSMatthew Dillon mrec->rec.leaf.data_len); 622f254e677STomohiro Kusumi } 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 699*005a4da7STomohiro Kusumi static 700*005a4da7STomohiro Kusumi void 70101a72c9fSMatthew Dillon create_pfs(const char *filesystem, uuid_t *s_uuid) 70201a72c9fSMatthew Dillon { 703f414d101SSascha Wildner if (ForceYesOpt == 1) { 70407485271SMichael Neumann fprintf(stderr, "PFS slave %s does not exist. " 70507485271SMichael Neumann "Auto create new slave PFS!\n", filesystem); 70607485271SMichael Neumann 707f414d101SSascha Wildner } else { 70801a72c9fSMatthew Dillon fprintf(stderr, "PFS slave %s does not exist.\n" 709fa9d9a9cSTomohiro Kusumi "Do you want to create a new slave PFS? [y/n] ", 71001a72c9fSMatthew Dillon filesystem); 71101a72c9fSMatthew Dillon fflush(stderr); 712052fd72bSTomohiro Kusumi if (getyntty() != 1) { 71302318f07STomohiro Kusumi errx(1, "Aborting operation"); 714052fd72bSTomohiro Kusumi /* not reached */ 715052fd72bSTomohiro Kusumi } 71607485271SMichael Neumann } 71701a72c9fSMatthew Dillon 71846137e17STomohiro Kusumi uint32_t status; 71901a72c9fSMatthew Dillon char *shared_uuid = NULL; 72001a72c9fSMatthew Dillon uuid_to_string(s_uuid, &shared_uuid, &status); 72101a72c9fSMatthew Dillon 72201a72c9fSMatthew Dillon char *cmd = NULL; 72301a72c9fSMatthew Dillon asprintf(&cmd, "/sbin/hammer pfs-slave '%s' shared-uuid=%s 1>&2", 72401a72c9fSMatthew Dillon filesystem, shared_uuid); 72501a72c9fSMatthew Dillon free(shared_uuid); 72601a72c9fSMatthew Dillon 727052fd72bSTomohiro Kusumi if (cmd == NULL) { 72802318f07STomohiro Kusumi errx(1, "Failed to alloc memory"); 729052fd72bSTomohiro Kusumi /* not reached */ 730052fd72bSTomohiro Kusumi } 73152e2f1b5STomohiro Kusumi if (system(cmd) != 0) 73201a72c9fSMatthew Dillon fprintf(stderr, "Failed to create PFS\n"); 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; 765578f0d56STomohiro Kusumi const 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 77788cdee70STomohiro Kusumi if (ac != 1) { 778a7fbbf91SMatthew Dillon mirror_usage(1); 77988cdee70STomohiro Kusumi /* not reached */ 78088cdee70STomohiro Kusumi } 781a7fbbf91SMatthew Dillon filesystem = av[0]; 78269f5a58cSMatthew Dillon hammer_check_restrict(filesystem); 783a7fbbf91SMatthew Dillon 78448eadef9SMatthew Dillon pickup.signature = 0; 78548eadef9SMatthew Dillon pickup.type = 0; 78648eadef9SMatthew Dillon 78748eadef9SMatthew Dillon again: 788a7fbbf91SMatthew Dillon bzero(&mirror, sizeof(mirror)); 789a7fbbf91SMatthew Dillon hammer_key_beg_init(&mirror.key_beg); 790a7fbbf91SMatthew Dillon hammer_key_end_init(&mirror.key_end); 79117dd83bcSMatthew Dillon mirror.key_end = mirror.key_beg; 792a7fbbf91SMatthew Dillon 79301a72c9fSMatthew Dillon /* 79401a72c9fSMatthew Dillon * Read initial packet 79501a72c9fSMatthew Dillon */ 79601a72c9fSMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 79701a72c9fSMatthew Dillon if (mrec == NULL) { 798052fd72bSTomohiro Kusumi if (error == 0) { 79902318f07STomohiro Kusumi errx(1, "validate_mrec_header: short read"); 800052fd72bSTomohiro Kusumi /* not reached */ 801052fd72bSTomohiro Kusumi } 80201a72c9fSMatthew Dillon exit(1); 80301a72c9fSMatthew Dillon } 80401a72c9fSMatthew Dillon /* 80501a72c9fSMatthew Dillon * Validate packet 80601a72c9fSMatthew Dillon */ 80701a72c9fSMatthew Dillon if (mrec->head.type == HAMMER_MREC_TYPE_TERM) { 808764d0c8cSTomohiro Kusumi free(buf); 80901a72c9fSMatthew Dillon return; 81001a72c9fSMatthew Dillon } 811052fd72bSTomohiro Kusumi if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) { 81202318f07STomohiro Kusumi errx(1, "validate_mrec_header: did not get expected " 81302318f07STomohiro Kusumi "PFSD record type"); 814052fd72bSTomohiro Kusumi /* not reached */ 815052fd72bSTomohiro Kusumi } 816052fd72bSTomohiro Kusumi if (mrec->head.rec_size != sizeof(mrec->pfs)) { 81702318f07STomohiro Kusumi errx(1, "validate_mrec_header: unexpected payload size"); 818052fd72bSTomohiro Kusumi /* not reached */ 819052fd72bSTomohiro Kusumi } 82001a72c9fSMatthew Dillon 82101a72c9fSMatthew Dillon /* 82201a72c9fSMatthew Dillon * Create slave PFS if it doesn't yet exist 82301a72c9fSMatthew Dillon */ 82452e2f1b5STomohiro Kusumi if (lstat(filesystem, &st) != 0) 82501a72c9fSMatthew Dillon create_pfs(filesystem, &mrec->pfs.pfsd.shared_uuid); 82601a72c9fSMatthew Dillon free(mrec); 82701a72c9fSMatthew Dillon mrec = NULL; 82801a72c9fSMatthew Dillon 829d4e5b69bSMatthew Dillon fd = getpfs(&pfs, filesystem); 830a7fbbf91SMatthew Dillon 831243ca327SMatthew Dillon /* 832d4e5b69bSMatthew Dillon * In two-way mode the target writes out a PFS packet first. 833d4e5b69bSMatthew Dillon * The source uses our tid_end as its tid_beg by default, 834d4e5b69bSMatthew Dillon * picking up where it left off. 835243ca327SMatthew Dillon */ 836d4e5b69bSMatthew Dillon mirror.tid_beg = 0; 837d4e5b69bSMatthew Dillon if (TwoWayPipeOpt) { 838e7f926a5SMatthew Dillon generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp); 839e7f926a5SMatthew Dillon if (mirror.tid_beg < mrec_tmp.pfs.pfsd.sync_beg_tid) 840e7f926a5SMatthew Dillon mirror.tid_beg = mrec_tmp.pfs.pfsd.sync_beg_tid; 841e7f926a5SMatthew Dillon mirror.tid_end = mrec_tmp.pfs.pfsd.sync_end_tid; 842e7f926a5SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_PFSD, 843e7f926a5SMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.pfs)); 844d4e5b69bSMatthew Dillon } 845d4e5b69bSMatthew Dillon 846d4e5b69bSMatthew Dillon /* 84717dd83bcSMatthew Dillon * Read and process the PFS header. The source informs us of 84817dd83bcSMatthew Dillon * the TID range the stream represents. 849d4e5b69bSMatthew Dillon */ 85048eadef9SMatthew Dillon n = validate_mrec_header(fd, 0, 1, pfs.pfs_id, &pickup, 851d4e5b69bSMatthew Dillon &mirror.tid_beg, &mirror.tid_end); 85248eadef9SMatthew Dillon if (n < 0) { /* got TERM record */ 85348eadef9SMatthew Dillon relpfs(fd, &pfs); 854764d0c8cSTomohiro Kusumi free(buf); 85548eadef9SMatthew Dillon return; 85648eadef9SMatthew Dillon } 85734ebae70SMatthew Dillon 858a7fbbf91SMatthew Dillon mirror.ubuf = buf; 859a7fbbf91SMatthew Dillon mirror.size = SERIALBUF_SIZE; 860a7fbbf91SMatthew Dillon 861243ca327SMatthew Dillon /* 86217dd83bcSMatthew Dillon * Read and process bulk records (REC, PASS, and SKIP types). 86317dd83bcSMatthew Dillon * 86417dd83bcSMatthew Dillon * On your life, do NOT mess with mirror.key_cur or your mirror 86517dd83bcSMatthew Dillon * target may become history. 866243ca327SMatthew Dillon */ 867a7fbbf91SMatthew Dillon for (;;) { 868a7fbbf91SMatthew Dillon mirror.count = 0; 869d4e5b69bSMatthew Dillon mirror.pfs_id = pfs.pfs_id; 870d4e5b69bSMatthew Dillon mirror.shared_uuid = pfs.ondisk->shared_uuid; 871a7fbbf91SMatthew Dillon mirror.size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup); 872a7fbbf91SMatthew Dillon if (mirror.size <= 0) 873a7fbbf91SMatthew Dillon break; 874052fd72bSTomohiro Kusumi if (ioctl(fd, HAMMERIOC_MIRROR_WRITE, &mirror) < 0) { 87502318f07STomohiro Kusumi err(1, "Mirror-write %s failed", filesystem); 876052fd72bSTomohiro Kusumi /* not reached */ 877052fd72bSTomohiro Kusumi } 878052fd72bSTomohiro Kusumi if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) { 87902318f07STomohiro Kusumi errx(1, "Mirror-write %s fatal error %d", 88017dd83bcSMatthew Dillon filesystem, mirror.head.error); 881052fd72bSTomohiro Kusumi /* not reached */ 882052fd72bSTomohiro Kusumi } 883243ca327SMatthew Dillon #if 0 884f254e677STomohiro Kusumi if (mirror.head.flags & HAMMER_IOC_HEAD_INTR) { 88502318f07STomohiro Kusumi errx(1, "Mirror-write %s interrupted by timer at" 88602318f07STomohiro Kusumi " %016llx", 887a7fbbf91SMatthew Dillon filesystem, 888243ca327SMatthew Dillon mirror.key_cur.obj_id); 889052fd72bSTomohiro Kusumi /* not reached */ 890f254e677STomohiro Kusumi } 891243ca327SMatthew Dillon #endif 892a7fbbf91SMatthew Dillon } 893243ca327SMatthew Dillon 894243ca327SMatthew Dillon /* 895243ca327SMatthew Dillon * Read and process the termination sync record. 896243ca327SMatthew Dillon */ 897243ca327SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 8989dc76cb1SMatthew Dillon 8999dc76cb1SMatthew Dillon if (mrec && mrec->head.type == HAMMER_MREC_TYPE_TERM) { 90048eadef9SMatthew Dillon fprintf(stderr, "Mirror-write: received termination request\n"); 901764d0c8cSTomohiro Kusumi relpfs(fd, &pfs); 90248eadef9SMatthew Dillon free(mrec); 903764d0c8cSTomohiro Kusumi free(buf); 9049dc76cb1SMatthew Dillon return; 9059dc76cb1SMatthew Dillon } 9069dc76cb1SMatthew Dillon 90717dd83bcSMatthew Dillon if (mrec == NULL || 90848eadef9SMatthew Dillon (mrec->head.type != HAMMER_MREC_TYPE_SYNC && 90948eadef9SMatthew Dillon mrec->head.type != HAMMER_MREC_TYPE_IDLE) || 910f254e677STomohiro Kusumi mrec->head.rec_size != sizeof(mrec->sync)) { 91102318f07STomohiro Kusumi errx(1, "Mirror-write %s: Did not get termination " 91202318f07STomohiro Kusumi "sync record, or rec_size is wrong rt=%d", 91302318f07STomohiro Kusumi filesystem, (mrec ? (int)mrec->head.type : -1)); 914052fd72bSTomohiro Kusumi /* not reached */ 915f254e677STomohiro Kusumi } 916243ca327SMatthew Dillon 917243ca327SMatthew Dillon /* 918243ca327SMatthew Dillon * Update the PFS info on the target so the user has visibility 91948eadef9SMatthew Dillon * into the new snapshot, and sync the target filesystem. 920243ca327SMatthew Dillon */ 92148eadef9SMatthew Dillon if (mrec->head.type == HAMMER_MREC_TYPE_SYNC) { 922d4e5b69bSMatthew Dillon update_pfs_snapshot(fd, mirror.tid_end, pfs.pfs_id); 923243ca327SMatthew Dillon 924243ca327SMatthew Dillon bzero(&synctid, sizeof(synctid)); 925243ca327SMatthew Dillon synctid.op = HAMMER_SYNCTID_SYNC2; 926243ca327SMatthew Dillon ioctl(fd, HAMMERIOC_SYNCTID, &synctid); 927243ca327SMatthew Dillon 928f254e677STomohiro Kusumi if (VerboseOpt >= 2) { 92948eadef9SMatthew Dillon fprintf(stderr, "Mirror-write %s: succeeded\n", 93048eadef9SMatthew Dillon filesystem); 93148eadef9SMatthew Dillon } 932f254e677STomohiro Kusumi } 93348eadef9SMatthew Dillon 93448eadef9SMatthew Dillon free(mrec); 93548eadef9SMatthew Dillon mrec = NULL; 936243ca327SMatthew Dillon 937243ca327SMatthew Dillon /* 938243ca327SMatthew Dillon * Report back to the originator. 939243ca327SMatthew Dillon */ 940243ca327SMatthew Dillon if (TwoWayPipeOpt) { 94117dd83bcSMatthew Dillon mrec_tmp.update.tid = mirror.tid_end; 942243ca327SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_UPDATE, 94317dd83bcSMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.update)); 944243ca327SMatthew Dillon } else { 945a276dc6bSMatthew Dillon printf("Source can update synctid to 0x%016jx\n", 946a276dc6bSMatthew Dillon (uintmax_t)mirror.tid_end); 947243ca327SMatthew Dillon } 94848eadef9SMatthew Dillon relpfs(fd, &pfs); 94948eadef9SMatthew Dillon goto again; 950243ca327SMatthew Dillon } 951243ca327SMatthew Dillon 952243ca327SMatthew Dillon void 9539f1b0121SAntonio Huete Jimenez hammer_cmd_mirror_dump(char **av, int ac) 954243ca327SMatthew Dillon { 955243ca327SMatthew Dillon char *buf = malloc(SERIALBUF_SIZE); 95617dd83bcSMatthew Dillon struct hammer_ioc_mrecord_head pickup; 95717dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 958243ca327SMatthew Dillon int error; 959243ca327SMatthew Dillon int size; 96017dd83bcSMatthew Dillon int offset; 96117dd83bcSMatthew Dillon int bytes; 9629f1b0121SAntonio Huete Jimenez int header_only = 0; 9639f1b0121SAntonio Huete Jimenez 96488cdee70STomohiro Kusumi if (ac == 1 && strcmp(*av, "header") == 0) { 9659f1b0121SAntonio Huete Jimenez header_only = 1; 96688cdee70STomohiro Kusumi } else if (ac != 0) { 9679f1b0121SAntonio Huete Jimenez mirror_usage(1); 96888cdee70STomohiro Kusumi /* not reached */ 96988cdee70STomohiro Kusumi } 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); 987764d0c8cSTomohiro 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); 1004052fd72bSTomohiro Kusumi if (offset + bytes > size) { 100502318f07STomohiro Kusumi errx(1, "Misaligned record"); 1006052fd72bSTomohiro Kusumi /* not reached */ 1007052fd72bSTomohiro Kusumi } 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: 1012ba8e5b0fSTomohiro Kusumi printf("Record lo=%08x obj=%016jx key=%016jx " 101385a8e8a7SMatthew Dillon "rt=%02x ot=%02x", 1014ba8e5b0fSTomohiro Kusumi mrec->rec.leaf.base.localization, 1015a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.obj_id, 1016a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.key, 101717dd83bcSMatthew Dillon mrec->rec.leaf.base.rec_type, 101817dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_type); 101985a8e8a7SMatthew Dillon if (mrec->head.type == 1020f254e677STomohiro Kusumi HAMMER_MREC_TYPE_REC_BADCRC) { 102185a8e8a7SMatthew Dillon printf(" (BAD CRC)"); 1022f254e677STomohiro Kusumi } 102385a8e8a7SMatthew Dillon printf("\n"); 1024a276dc6bSMatthew Dillon printf(" tids %016jx:%016jx data=%d\n", 1025a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.create_tid, 1026a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.delete_tid, 102717dd83bcSMatthew Dillon mrec->rec.leaf.data_len); 102817dd83bcSMatthew Dillon break; 102917dd83bcSMatthew Dillon case HAMMER_MREC_TYPE_PASS: 1030ba8e5b0fSTomohiro Kusumi printf("Pass lo=%08x obj=%016jx key=%016jx " 103117dd83bcSMatthew Dillon "rt=%02x ot=%02x\n", 1032ba8e5b0fSTomohiro Kusumi mrec->rec.leaf.base.localization, 1033a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.obj_id, 1034a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.key, 103517dd83bcSMatthew Dillon mrec->rec.leaf.base.rec_type, 103617dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_type); 1037a276dc6bSMatthew Dillon printf(" tids %016jx:%016jx data=%d\n", 1038a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.create_tid, 1039a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.delete_tid, 104017dd83bcSMatthew Dillon mrec->rec.leaf.data_len); 104117dd83bcSMatthew Dillon break; 104217dd83bcSMatthew Dillon case HAMMER_MREC_TYPE_SKIP: 1043ba8e5b0fSTomohiro Kusumi printf("Skip lo=%08x obj=%016jx key=%016jx rt=%02x to\n" 1044ba8e5b0fSTomohiro Kusumi " lo=%08x obj=%016jx key=%016jx rt=%02x\n", 1045ba8e5b0fSTomohiro Kusumi mrec->skip.skip_beg.localization, 1046a276dc6bSMatthew Dillon (uintmax_t)mrec->skip.skip_beg.obj_id, 1047a276dc6bSMatthew Dillon (uintmax_t)mrec->skip.skip_beg.key, 104817dd83bcSMatthew Dillon mrec->skip.skip_beg.rec_type, 1049ba8e5b0fSTomohiro Kusumi mrec->skip.skip_end.localization, 1050a276dc6bSMatthew Dillon (uintmax_t)mrec->skip.skip_end.obj_id, 1051a276dc6bSMatthew Dillon (uintmax_t)mrec->skip.skip_end.key, 105217dd83bcSMatthew Dillon mrec->skip.skip_end.rec_type); 105317dd83bcSMatthew Dillon default: 105417dd83bcSMatthew Dillon break; 105517dd83bcSMatthew Dillon } 105617dd83bcSMatthew Dillon offset += bytes; 1057243ca327SMatthew Dillon } 1058243ca327SMatthew Dillon } 1059243ca327SMatthew Dillon 1060243ca327SMatthew Dillon /* 1061243ca327SMatthew Dillon * Read and process the termination sync record. 1062243ca327SMatthew Dillon */ 1063243ca327SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 106448eadef9SMatthew Dillon if (mrec == NULL || 106548eadef9SMatthew Dillon (mrec->head.type != HAMMER_MREC_TYPE_SYNC && 1066f254e677STomohiro Kusumi mrec->head.type != HAMMER_MREC_TYPE_IDLE)) { 1067243ca327SMatthew Dillon fprintf(stderr, "Mirror-dump: Did not get termination " 1068243ca327SMatthew Dillon "sync record\n"); 1069f254e677STomohiro Kusumi } 1070cc71ff00STomohiro Kusumi free(mrec); 107139e88285SMatthew Dillon 107239e88285SMatthew Dillon /* 107339e88285SMatthew Dillon * Continue with more batches until EOF. 107439e88285SMatthew Dillon */ 107539e88285SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 1076cc71ff00STomohiro Kusumi if (mrec) { 1077cc71ff00STomohiro Kusumi free(mrec); 107839e88285SMatthew Dillon goto again; 1079a7fbbf91SMatthew Dillon } 1080764d0c8cSTomohiro Kusumi free(buf); 1081cc71ff00STomohiro Kusumi } 1082a7fbbf91SMatthew Dillon 1083a7fbbf91SMatthew Dillon void 108448eadef9SMatthew Dillon hammer_cmd_mirror_copy(char **av, int ac, int streaming) 1085a7fbbf91SMatthew Dillon { 108634ebae70SMatthew Dillon pid_t pid1; 108734ebae70SMatthew Dillon pid_t pid2; 108834ebae70SMatthew Dillon int fds[2]; 108908b71f9fSMatthew Dillon const char *xav[32]; 1090243ca327SMatthew Dillon char tbuf[16]; 1091e2c596b1SChris Turner char *sh, *user, *host, *rfs; 1092243ca327SMatthew Dillon int xac; 109334ebae70SMatthew Dillon 109488cdee70STomohiro Kusumi if (ac != 2) { 109534ebae70SMatthew Dillon mirror_usage(1); 109688cdee70STomohiro Kusumi /* not reached */ 109788cdee70STomohiro Kusumi } 109834ebae70SMatthew Dillon 1099e7f926a5SMatthew Dillon TwoWayPipeOpt = 1; 11000bd7a37cSMatthew Dillon signal(SIGPIPE, SIG_IGN); 1101e7f926a5SMatthew Dillon 1102e7f926a5SMatthew Dillon again: 1103052fd72bSTomohiro Kusumi if (pipe(fds) < 0) { 110402318f07STomohiro Kusumi err(1, "pipe"); 1105052fd72bSTomohiro Kusumi /* not reached */ 1106052fd72bSTomohiro Kusumi } 110734ebae70SMatthew Dillon 110834ebae70SMatthew Dillon /* 110934ebae70SMatthew Dillon * Source 111034ebae70SMatthew Dillon */ 111134ebae70SMatthew Dillon if ((pid1 = fork()) == 0) { 11120bd7a37cSMatthew Dillon signal(SIGPIPE, SIG_DFL); 111334ebae70SMatthew Dillon dup2(fds[0], 0); 111434ebae70SMatthew Dillon dup2(fds[0], 1); 111534ebae70SMatthew Dillon close(fds[0]); 111634ebae70SMatthew Dillon close(fds[1]); 1117e2c596b1SChris Turner if ((rfs = strchr(av[0], ':')) != NULL) { 1118243ca327SMatthew Dillon xac = 0; 1119e2c596b1SChris Turner 1120e2c596b1SChris Turner if((sh = getenv("HAMMER_RSH")) == NULL) 1121243ca327SMatthew Dillon xav[xac++] = "ssh"; 1122e2c596b1SChris Turner else 1123e2c596b1SChris Turner xav[xac++] = sh; 1124e2c596b1SChris Turner 11253a998207SMatthew Dillon if (CompressOpt) 11263a998207SMatthew Dillon xav[xac++] = "-C"; 1127e2c596b1SChris Turner 1128e2c596b1SChris Turner if ((host = strchr(av[0], '@')) != NULL) { 1129e2c596b1SChris Turner user = strndup( av[0], (host++ - av[0])); 1130e2c596b1SChris Turner host = strndup( host, (rfs++ - host)); 1131e2c596b1SChris Turner xav[xac++] = "-l"; 1132e2c596b1SChris Turner xav[xac++] = user; 1133e2c596b1SChris Turner xav[xac++] = host; 113452e2f1b5STomohiro Kusumi } else { 1135e2c596b1SChris Turner host = strndup( av[0], (rfs++ - av[0])); 1136e2c596b1SChris Turner user = NULL; 1137e2c596b1SChris Turner xav[xac++] = host; 1138e2c596b1SChris Turner } 1139e2c596b1SChris Turner 1140e2c596b1SChris Turner 11416c45ca3eSMatthew Dillon if (SshPort) { 11426c45ca3eSMatthew Dillon xav[xac++] = "-p"; 11436c45ca3eSMatthew Dillon xav[xac++] = SshPort; 11446c45ca3eSMatthew Dillon } 1145e2c596b1SChris Turner 1146243ca327SMatthew Dillon xav[xac++] = "hammer"; 114748eadef9SMatthew Dillon 114848eadef9SMatthew Dillon switch(VerboseOpt) { 114948eadef9SMatthew Dillon case 0: 115048eadef9SMatthew Dillon break; 115148eadef9SMatthew Dillon case 1: 1152243ca327SMatthew Dillon xav[xac++] = "-v"; 115348eadef9SMatthew Dillon break; 115448eadef9SMatthew Dillon case 2: 115548eadef9SMatthew Dillon xav[xac++] = "-vv"; 115648eadef9SMatthew Dillon break; 115748eadef9SMatthew Dillon default: 115848eadef9SMatthew Dillon xav[xac++] = "-vvv"; 115948eadef9SMatthew Dillon break; 116048eadef9SMatthew Dillon } 116152e2f1b5STomohiro Kusumi if (ForceYesOpt) 116207485271SMichael Neumann xav[xac++] = "-y"; 1163243ca327SMatthew Dillon xav[xac++] = "-2"; 1164243ca327SMatthew Dillon if (TimeoutOpt) { 1165243ca327SMatthew Dillon snprintf(tbuf, sizeof(tbuf), "%d", TimeoutOpt); 1166243ca327SMatthew Dillon xav[xac++] = "-t"; 1167243ca327SMatthew Dillon xav[xac++] = tbuf; 1168243ca327SMatthew Dillon } 116908b71f9fSMatthew Dillon if (SplitupOptStr) { 117008b71f9fSMatthew Dillon xav[xac++] = "-S"; 117108b71f9fSMatthew Dillon xav[xac++] = SplitupOptStr; 117208b71f9fSMatthew Dillon } 117348eadef9SMatthew Dillon if (streaming) 1174901f434aSMatthew Dillon xav[xac++] = "mirror-read-stream"; 117548eadef9SMatthew Dillon else 1176243ca327SMatthew Dillon xav[xac++] = "mirror-read"; 1177e2c596b1SChris Turner xav[xac++] = rfs; 1178243ca327SMatthew Dillon xav[xac++] = NULL; 1179e2c596b1SChris Turner execvp(*xav, (void *)xav); 118034ebae70SMatthew Dillon } else { 118148eadef9SMatthew Dillon hammer_cmd_mirror_read(av, 1, streaming); 1182243ca327SMatthew Dillon fflush(stdout); 1183243ca327SMatthew Dillon fflush(stderr); 118434ebae70SMatthew Dillon } 118553d93cc7SMatthew Dillon _exit(1); 118634ebae70SMatthew Dillon } 118734ebae70SMatthew Dillon 118834ebae70SMatthew Dillon /* 118934ebae70SMatthew Dillon * Target 119034ebae70SMatthew Dillon */ 119134ebae70SMatthew Dillon if ((pid2 = fork()) == 0) { 11920bd7a37cSMatthew Dillon signal(SIGPIPE, SIG_DFL); 119334ebae70SMatthew Dillon dup2(fds[1], 0); 119434ebae70SMatthew Dillon dup2(fds[1], 1); 119534ebae70SMatthew Dillon close(fds[0]); 119634ebae70SMatthew Dillon close(fds[1]); 1197e2c596b1SChris Turner if ((rfs = strchr(av[1], ':')) != NULL) { 1198243ca327SMatthew Dillon xac = 0; 1199e2c596b1SChris Turner 1200e2c596b1SChris Turner if((sh = getenv("HAMMER_RSH")) == NULL) 1201243ca327SMatthew Dillon xav[xac++] = "ssh"; 1202e2c596b1SChris Turner else 1203e2c596b1SChris Turner xav[xac++] = sh; 1204e2c596b1SChris Turner 12053a998207SMatthew Dillon if (CompressOpt) 12063a998207SMatthew Dillon xav[xac++] = "-C"; 1207e2c596b1SChris Turner 1208e2c596b1SChris Turner if ((host = strchr(av[1], '@')) != NULL) { 1209e2c596b1SChris Turner user = strndup( av[1], (host++ - av[1])); 1210e2c596b1SChris Turner host = strndup( host, (rfs++ - host)); 1211e2c596b1SChris Turner xav[xac++] = "-l"; 1212e2c596b1SChris Turner xav[xac++] = user; 1213e2c596b1SChris Turner xav[xac++] = host; 121452e2f1b5STomohiro Kusumi } else { 1215e2c596b1SChris Turner host = strndup( av[1], (rfs++ - av[1])); 1216e2c596b1SChris Turner user = NULL; 1217e2c596b1SChris Turner xav[xac++] = host; 1218e2c596b1SChris Turner } 1219e2c596b1SChris Turner 12206c45ca3eSMatthew Dillon if (SshPort) { 12216c45ca3eSMatthew Dillon xav[xac++] = "-p"; 12226c45ca3eSMatthew Dillon xav[xac++] = SshPort; 12236c45ca3eSMatthew Dillon } 1224e2c596b1SChris Turner 1225243ca327SMatthew Dillon xav[xac++] = "hammer"; 122648eadef9SMatthew Dillon 122748eadef9SMatthew Dillon switch(VerboseOpt) { 122848eadef9SMatthew Dillon case 0: 122948eadef9SMatthew Dillon break; 123048eadef9SMatthew Dillon case 1: 1231243ca327SMatthew Dillon xav[xac++] = "-v"; 123248eadef9SMatthew Dillon break; 123348eadef9SMatthew Dillon case 2: 123448eadef9SMatthew Dillon xav[xac++] = "-vv"; 123548eadef9SMatthew Dillon break; 123648eadef9SMatthew Dillon default: 123748eadef9SMatthew Dillon xav[xac++] = "-vvv"; 123848eadef9SMatthew Dillon break; 123948eadef9SMatthew Dillon } 124052e2f1b5STomohiro Kusumi if (ForceYesOpt) 124107485271SMichael Neumann xav[xac++] = "-y"; 1242243ca327SMatthew Dillon xav[xac++] = "-2"; 1243243ca327SMatthew Dillon xav[xac++] = "mirror-write"; 1244e2c596b1SChris Turner xav[xac++] = rfs; 1245243ca327SMatthew Dillon xav[xac++] = NULL; 1246e2c596b1SChris Turner execvp(*xav, (void *)xav); 124734ebae70SMatthew Dillon } else { 124834ebae70SMatthew Dillon hammer_cmd_mirror_write(av + 1, 1); 1249243ca327SMatthew Dillon fflush(stdout); 1250243ca327SMatthew Dillon fflush(stderr); 125134ebae70SMatthew Dillon } 125253d93cc7SMatthew Dillon _exit(1); 125334ebae70SMatthew Dillon } 125434ebae70SMatthew Dillon close(fds[0]); 125534ebae70SMatthew Dillon close(fds[1]); 125634ebae70SMatthew Dillon 125734ebae70SMatthew Dillon while (waitpid(pid1, NULL, 0) <= 0) 125834ebae70SMatthew Dillon ; 125934ebae70SMatthew Dillon while (waitpid(pid2, NULL, 0) <= 0) 126034ebae70SMatthew Dillon ; 1261e7f926a5SMatthew Dillon 1262e7f926a5SMatthew Dillon /* 1263e7f926a5SMatthew Dillon * If the link is lost restart 1264e7f926a5SMatthew Dillon */ 1265e7f926a5SMatthew Dillon if (streaming) { 1266e7f926a5SMatthew Dillon if (VerboseOpt) { 1267e7f926a5SMatthew Dillon fprintf(stderr, "\nLost Link\n"); 1268e7f926a5SMatthew Dillon fflush(stderr); 1269e7f926a5SMatthew Dillon } 12700bd7a37cSMatthew Dillon sleep(15 + DelayOpt); 1271e7f926a5SMatthew Dillon goto again; 1272e7f926a5SMatthew Dillon } 1273e7f926a5SMatthew Dillon 1274a7fbbf91SMatthew Dillon } 1275a7fbbf91SMatthew Dillon 1276243ca327SMatthew Dillon /* 1277243ca327SMatthew Dillon * Read and return multiple mrecords 1278243ca327SMatthew Dillon */ 1279*005a4da7STomohiro Kusumi static 1280*005a4da7STomohiro Kusumi int 128117dd83bcSMatthew Dillon read_mrecords(int fd, char *buf, u_int size, hammer_ioc_mrecord_head_t pickup) 1282a7fbbf91SMatthew Dillon { 128317dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 1284a7fbbf91SMatthew Dillon u_int count; 1285a7fbbf91SMatthew Dillon size_t n; 1286a7fbbf91SMatthew Dillon size_t i; 128717dd83bcSMatthew Dillon size_t bytes; 128885a8e8a7SMatthew Dillon int type; 1289a7fbbf91SMatthew Dillon 1290a7fbbf91SMatthew Dillon count = 0; 1291a7fbbf91SMatthew Dillon while (size - count >= HAMMER_MREC_HEADSIZE) { 1292a7fbbf91SMatthew Dillon /* 1293a7fbbf91SMatthew Dillon * Cached the record header in case we run out of buffer 1294a7fbbf91SMatthew Dillon * space. 1295a7fbbf91SMatthew Dillon */ 129617dd83bcSMatthew Dillon fflush(stdout); 1297a7fbbf91SMatthew Dillon if (pickup->signature == 0) { 1298a7fbbf91SMatthew Dillon for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) { 1299a7fbbf91SMatthew Dillon i = read(fd, (char *)pickup + n, 1300a7fbbf91SMatthew Dillon HAMMER_MREC_HEADSIZE - n); 1301a7fbbf91SMatthew Dillon if (i <= 0) 1302a7fbbf91SMatthew Dillon break; 1303a7fbbf91SMatthew Dillon } 1304a7fbbf91SMatthew Dillon if (n == 0) 1305a7fbbf91SMatthew Dillon break; 1306052fd72bSTomohiro Kusumi if (n != HAMMER_MREC_HEADSIZE) { 130702318f07STomohiro Kusumi errx(1, "read_mrecords: short read on pipe"); 1308052fd72bSTomohiro Kusumi /* not reached */ 1309052fd72bSTomohiro Kusumi } 1310052fd72bSTomohiro Kusumi if (pickup->signature != HAMMER_IOC_MIRROR_SIGNATURE) { 131102318f07STomohiro Kusumi errx(1, "read_mrecords: malformed record on pipe, " 131202318f07STomohiro Kusumi "bad signature"); 1313052fd72bSTomohiro Kusumi /* not reached */ 1314052fd72bSTomohiro Kusumi } 1315a7fbbf91SMatthew Dillon } 1316a7fbbf91SMatthew Dillon if (pickup->rec_size < HAMMER_MREC_HEADSIZE || 1317052fd72bSTomohiro Kusumi pickup->rec_size > sizeof(*mrec) + HAMMER_XBUFSIZE) { 131802318f07STomohiro Kusumi errx(1, "read_mrecords: malformed record on pipe, " 131902318f07STomohiro Kusumi "illegal rec_size"); 1320052fd72bSTomohiro Kusumi /* not reached */ 1321052fd72bSTomohiro Kusumi } 1322a7fbbf91SMatthew Dillon 1323a7fbbf91SMatthew Dillon /* 1324a7fbbf91SMatthew Dillon * Stop if we have insufficient space for the record and data. 1325a7fbbf91SMatthew Dillon */ 132617dd83bcSMatthew Dillon bytes = HAMMER_HEAD_DOALIGN(pickup->rec_size); 132717dd83bcSMatthew Dillon if (size - count < bytes) 1328a7fbbf91SMatthew Dillon break; 1329a7fbbf91SMatthew Dillon 1330a7fbbf91SMatthew Dillon /* 133185a8e8a7SMatthew Dillon * Stop if the record type is not a REC, SKIP, or PASS, 133285a8e8a7SMatthew Dillon * which are the only types the ioctl supports. Other types 133385a8e8a7SMatthew Dillon * are used only by the userland protocol. 133485a8e8a7SMatthew Dillon * 133585a8e8a7SMatthew Dillon * Ignore all flags. 1336243ca327SMatthew Dillon */ 133785a8e8a7SMatthew Dillon type = pickup->type & HAMMER_MRECF_TYPE_LOMASK; 133885a8e8a7SMatthew Dillon if (type != HAMMER_MREC_TYPE_PFSD && 133985a8e8a7SMatthew Dillon type != HAMMER_MREC_TYPE_REC && 134085a8e8a7SMatthew Dillon type != HAMMER_MREC_TYPE_SKIP && 1341f254e677STomohiro Kusumi type != HAMMER_MREC_TYPE_PASS) { 1342243ca327SMatthew Dillon break; 1343f254e677STomohiro Kusumi } 1344243ca327SMatthew Dillon 1345243ca327SMatthew Dillon /* 1346a7fbbf91SMatthew Dillon * Read the remainder and clear the pickup signature. 1347a7fbbf91SMatthew Dillon */ 134817dd83bcSMatthew Dillon for (n = HAMMER_MREC_HEADSIZE; n < bytes; n += i) { 134917dd83bcSMatthew Dillon i = read(fd, buf + count + n, bytes - n); 1350a7fbbf91SMatthew Dillon if (i <= 0) 1351a7fbbf91SMatthew Dillon break; 1352a7fbbf91SMatthew Dillon } 1353052fd72bSTomohiro Kusumi if (n != bytes) { 135402318f07STomohiro Kusumi errx(1, "read_mrecords: short read on pipe"); 1355052fd72bSTomohiro Kusumi /* not reached */ 1356052fd72bSTomohiro Kusumi } 135717dd83bcSMatthew Dillon 135817dd83bcSMatthew Dillon bcopy(pickup, buf + count, HAMMER_MREC_HEADSIZE); 135917dd83bcSMatthew Dillon pickup->signature = 0; 136017dd83bcSMatthew Dillon pickup->type = 0; 136117dd83bcSMatthew Dillon mrec = (void *)(buf + count); 136217dd83bcSMatthew Dillon 136317dd83bcSMatthew Dillon /* 136417dd83bcSMatthew Dillon * Validate the completed record 136517dd83bcSMatthew Dillon */ 1366052fd72bSTomohiro Kusumi if (!hammer_crc_test_mrec_head(&mrec->head, mrec->head.rec_size)) { 136702318f07STomohiro Kusumi errx(1, "read_mrecords: malformed record on pipe, bad crc"); 1368052fd72bSTomohiro Kusumi /* not reached */ 1369052fd72bSTomohiro Kusumi } 1370a7fbbf91SMatthew Dillon 137117dd83bcSMatthew Dillon /* 137285a8e8a7SMatthew Dillon * If its a B-Tree record validate the data crc. 137385a8e8a7SMatthew Dillon * 137485a8e8a7SMatthew Dillon * NOTE: If the VFS passes us an explicitly errorde mrec 137585a8e8a7SMatthew Dillon * we just pass it through. 137617dd83bcSMatthew Dillon */ 137785a8e8a7SMatthew Dillon type = mrec->head.type & HAMMER_MRECF_TYPE_MASK; 137885a8e8a7SMatthew Dillon 137985a8e8a7SMatthew Dillon if (type == HAMMER_MREC_TYPE_REC) { 138017dd83bcSMatthew Dillon if (mrec->head.rec_size < 1381f254e677STomohiro Kusumi sizeof(mrec->rec) + mrec->rec.leaf.data_len) { 138202318f07STomohiro Kusumi errx(1, "read_mrecords: malformed record on " 138302318f07STomohiro Kusumi "pipe, illegal element data_len"); 1384052fd72bSTomohiro Kusumi /* not reached */ 1385f254e677STomohiro Kusumi } 138617dd83bcSMatthew Dillon if (mrec->rec.leaf.data_len && 138717dd83bcSMatthew Dillon mrec->rec.leaf.data_offset && 13884c09d9c4SMatthew Dillon hammer_crc_test_leaf(HammerVersion, &mrec->rec + 1, &mrec->rec.leaf) == 0) { 138917dd83bcSMatthew Dillon fprintf(stderr, 139017dd83bcSMatthew Dillon "read_mrecords: data_crc did not " 1391a276dc6bSMatthew Dillon "match data! obj=%016jx key=%016jx\n", 1392a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.obj_id, 1393a276dc6bSMatthew Dillon (uintmax_t)mrec->rec.leaf.base.key); 139417dd83bcSMatthew Dillon fprintf(stderr, 139517dd83bcSMatthew Dillon "continuing, but there are problems\n"); 139617dd83bcSMatthew Dillon } 139717dd83bcSMatthew Dillon } 139817dd83bcSMatthew Dillon count += bytes; 1399a7fbbf91SMatthew Dillon } 1400a7fbbf91SMatthew Dillon return(count); 1401a7fbbf91SMatthew Dillon } 1402a7fbbf91SMatthew Dillon 140334ebae70SMatthew Dillon /* 140417dd83bcSMatthew Dillon * Read and return a single mrecord. 1405243ca327SMatthew Dillon */ 1406243ca327SMatthew Dillon static 140717dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t 140817dd83bcSMatthew Dillon read_mrecord(int fdin, int *errorp, hammer_ioc_mrecord_head_t pickup) 1409243ca327SMatthew Dillon { 141017dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 141117dd83bcSMatthew Dillon struct hammer_ioc_mrecord_head mrechd; 1412243ca327SMatthew Dillon size_t bytes; 1413243ca327SMatthew Dillon size_t n; 1414243ca327SMatthew Dillon size_t i; 1415243ca327SMatthew Dillon 1416243ca327SMatthew Dillon if (pickup && pickup->type != 0) { 1417243ca327SMatthew Dillon mrechd = *pickup; 1418243ca327SMatthew Dillon pickup->signature = 0; 1419243ca327SMatthew Dillon pickup->type = 0; 1420243ca327SMatthew Dillon n = HAMMER_MREC_HEADSIZE; 1421243ca327SMatthew Dillon } else { 1422243ca327SMatthew Dillon /* 1423243ca327SMatthew Dillon * Read in the PFSD header from the sender. 1424243ca327SMatthew Dillon */ 1425243ca327SMatthew Dillon for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) { 1426243ca327SMatthew Dillon i = read(fdin, (char *)&mrechd + n, HAMMER_MREC_HEADSIZE - n); 1427243ca327SMatthew Dillon if (i <= 0) 1428243ca327SMatthew Dillon break; 1429243ca327SMatthew Dillon } 1430243ca327SMatthew Dillon if (n == 0) { 1431243ca327SMatthew Dillon *errorp = 0; /* EOF */ 1432243ca327SMatthew Dillon return(NULL); 1433243ca327SMatthew Dillon } 1434243ca327SMatthew Dillon if (n != HAMMER_MREC_HEADSIZE) { 1435243ca327SMatthew Dillon fprintf(stderr, "short read of mrecord header\n"); 1436243ca327SMatthew Dillon *errorp = EPIPE; 1437243ca327SMatthew Dillon return(NULL); 1438243ca327SMatthew Dillon } 1439243ca327SMatthew Dillon } 1440243ca327SMatthew Dillon if (mrechd.signature != HAMMER_IOC_MIRROR_SIGNATURE) { 1441243ca327SMatthew Dillon fprintf(stderr, "read_mrecord: bad signature\n"); 1442243ca327SMatthew Dillon *errorp = EINVAL; 1443243ca327SMatthew Dillon return(NULL); 1444243ca327SMatthew Dillon } 144517dd83bcSMatthew Dillon bytes = HAMMER_HEAD_DOALIGN(mrechd.rec_size); 144617dd83bcSMatthew Dillon assert(bytes >= sizeof(mrechd)); 1447243ca327SMatthew Dillon mrec = malloc(bytes); 144817dd83bcSMatthew Dillon mrec->head = mrechd; 144917dd83bcSMatthew Dillon 1450243ca327SMatthew Dillon while (n < bytes) { 1451243ca327SMatthew Dillon i = read(fdin, (char *)mrec + n, bytes - n); 1452243ca327SMatthew Dillon if (i <= 0) 1453243ca327SMatthew Dillon break; 1454243ca327SMatthew Dillon n += i; 1455243ca327SMatthew Dillon } 1456243ca327SMatthew Dillon if (n != bytes) { 1457243ca327SMatthew Dillon fprintf(stderr, "read_mrecord: short read on payload\n"); 1458243ca327SMatthew Dillon *errorp = EPIPE; 1459243ca327SMatthew Dillon return(NULL); 1460243ca327SMatthew Dillon } 146145643966STomohiro Kusumi if (!hammer_crc_test_mrec_head(&mrec->head, mrec->head.rec_size)) { 1462243ca327SMatthew Dillon fprintf(stderr, "read_mrecord: bad CRC\n"); 1463243ca327SMatthew Dillon *errorp = EINVAL; 1464243ca327SMatthew Dillon return(NULL); 1465243ca327SMatthew Dillon } 1466243ca327SMatthew Dillon *errorp = 0; 1467243ca327SMatthew Dillon return(mrec); 1468243ca327SMatthew Dillon } 1469243ca327SMatthew Dillon 1470243ca327SMatthew Dillon static 1471243ca327SMatthew Dillon void 147246137e17STomohiro Kusumi write_mrecord(int fdout, uint32_t type, hammer_ioc_mrecord_any_t mrec, 147317dd83bcSMatthew Dillon int bytes) 1474243ca327SMatthew Dillon { 147517dd83bcSMatthew Dillon char zbuf[HAMMER_HEAD_ALIGN]; 147617dd83bcSMatthew Dillon int pad; 1477243ca327SMatthew Dillon 147817dd83bcSMatthew Dillon pad = HAMMER_HEAD_DOALIGN(bytes) - bytes; 147917dd83bcSMatthew Dillon 148017dd83bcSMatthew Dillon assert(bytes >= (int)sizeof(mrec->head)); 148117dd83bcSMatthew Dillon bzero(&mrec->head, sizeof(mrec->head)); 148217dd83bcSMatthew Dillon mrec->head.signature = HAMMER_IOC_MIRROR_SIGNATURE; 148317dd83bcSMatthew Dillon mrec->head.type = type; 148417dd83bcSMatthew Dillon mrec->head.rec_size = bytes; 148545643966STomohiro Kusumi hammer_crc_set_mrec_head(&mrec->head, bytes); 1486052fd72bSTomohiro Kusumi if (write(fdout, mrec, bytes) != bytes) { 148702318f07STomohiro Kusumi err(1, "write_mrecord"); 1488052fd72bSTomohiro Kusumi /* not reached */ 1489052fd72bSTomohiro Kusumi } 149017dd83bcSMatthew Dillon if (pad) { 149117dd83bcSMatthew Dillon bzero(zbuf, pad); 1492052fd72bSTomohiro Kusumi if (write(fdout, zbuf, pad) != pad) { 149302318f07STomohiro Kusumi err(1, "write_mrecord"); 1494052fd72bSTomohiro Kusumi /* not reached */ 1495052fd72bSTomohiro Kusumi } 149617dd83bcSMatthew Dillon } 1497243ca327SMatthew Dillon } 1498243ca327SMatthew Dillon 1499243ca327SMatthew Dillon /* 150034ebae70SMatthew Dillon * Generate a mirroring header with the pfs information of the 150134ebae70SMatthew Dillon * originating filesytem. 150234ebae70SMatthew Dillon */ 1503*005a4da7STomohiro Kusumi static 1504*005a4da7STomohiro Kusumi void 1505e7f926a5SMatthew Dillon generate_mrec_header(int fd, int pfs_id, 1506e7f926a5SMatthew Dillon union hammer_ioc_mrecord_any *mrec_tmp) 150734ebae70SMatthew Dillon { 150834ebae70SMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 150934ebae70SMatthew Dillon 1510e7f926a5SMatthew Dillon bzero(mrec_tmp, sizeof(*mrec_tmp)); 15111b23fc22STomohiro Kusumi clrpfs(&pfs, &mrec_tmp->pfs.pfsd, pfs_id); 15121b23fc22STomohiro Kusumi 1513052fd72bSTomohiro Kusumi if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 151402318f07STomohiro Kusumi err(1, "Mirror-read: not a HAMMER fs/pseudofs!"); 1515052fd72bSTomohiro Kusumi /* not reached */ 1516052fd72bSTomohiro Kusumi } 1517052fd72bSTomohiro Kusumi if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) { 151802318f07STomohiro Kusumi errx(1, "Mirror-read: HAMMER PFS version mismatch!"); 1519052fd72bSTomohiro Kusumi /* not reached */ 1520052fd72bSTomohiro Kusumi } 1521e7f926a5SMatthew Dillon mrec_tmp->pfs.version = pfs.version; 152234ebae70SMatthew Dillon } 152334ebae70SMatthew Dillon 152434ebae70SMatthew Dillon /* 152534ebae70SMatthew Dillon * Validate the pfs information from the originating filesystem 152634ebae70SMatthew Dillon * against the target filesystem. shared_uuid must match. 152748eadef9SMatthew Dillon * 152848eadef9SMatthew Dillon * return -1 if we got a TERM record 152934ebae70SMatthew Dillon */ 1530*005a4da7STomohiro Kusumi static 1531*005a4da7STomohiro Kusumi int 1532d4e5b69bSMatthew Dillon validate_mrec_header(int fd, int fdin, int is_target, int pfs_id, 153348eadef9SMatthew Dillon struct hammer_ioc_mrecord_head *pickup, 153434ebae70SMatthew Dillon hammer_tid_t *tid_begp, hammer_tid_t *tid_endp) 153534ebae70SMatthew Dillon { 153634ebae70SMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 153734ebae70SMatthew Dillon struct hammer_pseudofs_data pfsd; 153817dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 1539243ca327SMatthew Dillon int error; 154034ebae70SMatthew Dillon 154134ebae70SMatthew Dillon /* 154234ebae70SMatthew Dillon * Get the PFSD info from the target filesystem. 154334ebae70SMatthew Dillon */ 15441b23fc22STomohiro Kusumi clrpfs(&pfs, &pfsd, pfs_id); 1545052fd72bSTomohiro Kusumi if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 154602318f07STomohiro Kusumi err(1, "mirror-write: not a HAMMER fs/pseudofs!"); 1547052fd72bSTomohiro Kusumi /* not reached */ 1548052fd72bSTomohiro Kusumi } 1549052fd72bSTomohiro Kusumi if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) { 155002318f07STomohiro Kusumi errx(1, "mirror-write: HAMMER PFS version mismatch!"); 1551052fd72bSTomohiro Kusumi /* not reached */ 1552052fd72bSTomohiro Kusumi } 155334ebae70SMatthew Dillon 155448eadef9SMatthew Dillon mrec = read_mrecord(fdin, &error, pickup); 1555243ca327SMatthew Dillon if (mrec == NULL) { 1556052fd72bSTomohiro Kusumi if (error == 0) { 155702318f07STomohiro Kusumi errx(1, "validate_mrec_header: short read"); 1558052fd72bSTomohiro Kusumi /* not reached */ 1559052fd72bSTomohiro Kusumi } 156034ebae70SMatthew Dillon exit(1); 156134ebae70SMatthew Dillon } 156248eadef9SMatthew Dillon if (mrec->head.type == HAMMER_MREC_TYPE_TERM) { 156348eadef9SMatthew Dillon free(mrec); 156448eadef9SMatthew Dillon return(-1); 156548eadef9SMatthew Dillon } 156648eadef9SMatthew Dillon 1567052fd72bSTomohiro Kusumi if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) { 156802318f07STomohiro Kusumi errx(1, "validate_mrec_header: did not get expected " 156902318f07STomohiro Kusumi "PFSD record type"); 1570052fd72bSTomohiro Kusumi /* not reached */ 1571052fd72bSTomohiro Kusumi } 1572052fd72bSTomohiro Kusumi if (mrec->head.rec_size != sizeof(mrec->pfs)) { 157302318f07STomohiro Kusumi errx(1, "validate_mrec_header: unexpected payload size"); 1574052fd72bSTomohiro Kusumi /* not reached */ 1575052fd72bSTomohiro Kusumi } 1576052fd72bSTomohiro Kusumi if (mrec->pfs.version != pfs.version) { 157702318f07STomohiro Kusumi errx(1, "validate_mrec_header: Version mismatch"); 1578052fd72bSTomohiro Kusumi /* not reached */ 1579052fd72bSTomohiro Kusumi } 158034ebae70SMatthew Dillon 158134ebae70SMatthew Dillon /* 158234ebae70SMatthew Dillon * Whew. Ok, is the read PFS info compatible with the target? 158334ebae70SMatthew Dillon */ 158417dd83bcSMatthew Dillon if (bcmp(&mrec->pfs.pfsd.shared_uuid, &pfsd.shared_uuid, 1585052fd72bSTomohiro Kusumi sizeof(pfsd.shared_uuid)) != 0) { 158602318f07STomohiro Kusumi errx(1, "mirror-write: source and target have " 158702318f07STomohiro Kusumi "different shared-uuid's!"); 1588052fd72bSTomohiro Kusumi /* not reached */ 1589052fd72bSTomohiro Kusumi } 1590052fd72bSTomohiro Kusumi if (is_target && hammer_is_pfs_master(&pfsd)) { 159102318f07STomohiro Kusumi errx(1, "mirror-write: target must be in slave mode"); 1592052fd72bSTomohiro Kusumi /* not reached */ 1593052fd72bSTomohiro Kusumi } 1594d4e5b69bSMatthew Dillon if (tid_begp) 159517dd83bcSMatthew Dillon *tid_begp = mrec->pfs.pfsd.sync_beg_tid; 1596d4e5b69bSMatthew Dillon if (tid_endp) 159717dd83bcSMatthew Dillon *tid_endp = mrec->pfs.pfsd.sync_end_tid; 1598243ca327SMatthew Dillon free(mrec); 159948eadef9SMatthew Dillon return(0); 160034ebae70SMatthew Dillon } 160134ebae70SMatthew Dillon 1602*005a4da7STomohiro Kusumi static 1603*005a4da7STomohiro Kusumi void 1604d4e5b69bSMatthew Dillon update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id) 160534ebae70SMatthew Dillon { 1606243ca327SMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 1607243ca327SMatthew Dillon struct hammer_pseudofs_data pfsd; 160834ebae70SMatthew Dillon 16091b23fc22STomohiro Kusumi clrpfs(&pfs, &pfsd, pfs_id); 1610052fd72bSTomohiro Kusumi if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 161102318f07STomohiro Kusumi err(1, "update_pfs_snapshot (read)"); 1612052fd72bSTomohiro Kusumi /* not reached */ 1613052fd72bSTomohiro Kusumi } 161402318f07STomohiro Kusumi 16159c67b4d2SMatthew Dillon if (pfsd.sync_end_tid != snapshot_tid) { 1616ddc8e722SMatthew Dillon pfsd.sync_end_tid = snapshot_tid; 1617052fd72bSTomohiro Kusumi if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) != 0) { 161802318f07STomohiro Kusumi err(1, "update_pfs_snapshot (rewrite)"); 1619052fd72bSTomohiro Kusumi /* not reached */ 1620052fd72bSTomohiro Kusumi } 162148eadef9SMatthew Dillon if (VerboseOpt >= 2) { 16229dc76cb1SMatthew Dillon fprintf(stderr, 1623ced4470dSMatthew Dillon "Mirror-write: Completed, updated snapshot " 1624a276dc6bSMatthew Dillon "to %016jx\n", 1625a276dc6bSMatthew Dillon (uintmax_t)snapshot_tid); 1626ced4470dSMatthew Dillon fflush(stderr); 1627243ca327SMatthew Dillon } 16289c67b4d2SMatthew Dillon } 162948eadef9SMatthew Dillon } 1630243ca327SMatthew Dillon 163148eadef9SMatthew Dillon /* 163248eadef9SMatthew Dillon * Bandwidth-limited write in chunks 163348eadef9SMatthew Dillon */ 163448eadef9SMatthew Dillon static 163548eadef9SMatthew Dillon ssize_t 163648eadef9SMatthew Dillon writebw(int fd, const void *buf, size_t nbytes, 163746137e17STomohiro Kusumi uint64_t *bwcount, struct timeval *tv1) 163848eadef9SMatthew Dillon { 163948eadef9SMatthew Dillon struct timeval tv2; 164048eadef9SMatthew Dillon size_t n; 164148eadef9SMatthew Dillon ssize_t r; 164248eadef9SMatthew Dillon ssize_t a; 164348eadef9SMatthew Dillon int usec; 164448eadef9SMatthew Dillon 164548eadef9SMatthew Dillon a = 0; 164648eadef9SMatthew Dillon r = 0; 164748eadef9SMatthew Dillon while (nbytes) { 164848eadef9SMatthew Dillon if (*bwcount + nbytes > BandwidthOpt) 164948eadef9SMatthew Dillon n = BandwidthOpt - *bwcount; 165048eadef9SMatthew Dillon else 165148eadef9SMatthew Dillon n = nbytes; 165248eadef9SMatthew Dillon if (n) 165348eadef9SMatthew Dillon r = write(fd, buf, n); 165448eadef9SMatthew Dillon if (r >= 0) { 165548eadef9SMatthew Dillon a += r; 165648eadef9SMatthew Dillon nbytes -= r; 165748eadef9SMatthew Dillon buf = (const char *)buf + r; 165848eadef9SMatthew Dillon } 165948eadef9SMatthew Dillon if ((size_t)r != n) 166048eadef9SMatthew Dillon break; 166148eadef9SMatthew Dillon *bwcount += n; 166248eadef9SMatthew Dillon if (*bwcount >= BandwidthOpt) { 166348eadef9SMatthew Dillon gettimeofday(&tv2, NULL); 166448eadef9SMatthew Dillon usec = (int)(tv2.tv_sec - tv1->tv_sec) * 1000000 + 166548eadef9SMatthew Dillon (int)(tv2.tv_usec - tv1->tv_usec); 166648eadef9SMatthew Dillon if (usec >= 0 && usec < 1000000) 166748eadef9SMatthew Dillon usleep(1000000 - usec); 166848eadef9SMatthew Dillon gettimeofday(tv1, NULL); 166948eadef9SMatthew Dillon *bwcount -= BandwidthOpt; 167048eadef9SMatthew Dillon } 167148eadef9SMatthew Dillon } 167248eadef9SMatthew Dillon return(a ? a : r); 167348eadef9SMatthew Dillon } 167434ebae70SMatthew Dillon 167501a72c9fSMatthew Dillon /* 167601a72c9fSMatthew Dillon * Get a yes or no answer from the terminal. The program may be run as 167701a72c9fSMatthew Dillon * part of a two-way pipe so we cannot use stdin for this operation. 167801a72c9fSMatthew Dillon */ 1679*005a4da7STomohiro Kusumi static 1680*005a4da7STomohiro Kusumi int 1681778c2d2cSTomohiro Kusumi getyntty(void) 168201a72c9fSMatthew Dillon { 168301a72c9fSMatthew Dillon char buf[256]; 168401a72c9fSMatthew Dillon FILE *fp; 168501a72c9fSMatthew Dillon int result; 168601a72c9fSMatthew Dillon 168701a72c9fSMatthew Dillon fp = fopen("/dev/tty", "r"); 168801a72c9fSMatthew Dillon if (fp == NULL) { 168901a72c9fSMatthew Dillon fprintf(stderr, "No terminal for response\n"); 169001a72c9fSMatthew Dillon return(-1); 169101a72c9fSMatthew Dillon } 169201a72c9fSMatthew Dillon result = -1; 169301a72c9fSMatthew Dillon while (fgets(buf, sizeof(buf), fp) != NULL) { 169401a72c9fSMatthew Dillon if (buf[0] == 'y' || buf[0] == 'Y') { 169501a72c9fSMatthew Dillon result = 1; 169601a72c9fSMatthew Dillon break; 169701a72c9fSMatthew Dillon } 169801a72c9fSMatthew Dillon if (buf[0] == 'n' || buf[0] == 'N') { 169901a72c9fSMatthew Dillon result = 0; 170001a72c9fSMatthew Dillon break; 170101a72c9fSMatthew Dillon } 170201a72c9fSMatthew Dillon fprintf(stderr, "Response not understood\n"); 170301a72c9fSMatthew Dillon break; 170401a72c9fSMatthew Dillon } 170501a72c9fSMatthew Dillon fclose(fp); 170601a72c9fSMatthew Dillon return(result); 170701a72c9fSMatthew Dillon } 170801a72c9fSMatthew Dillon 1709*005a4da7STomohiro Kusumi static 1710*005a4da7STomohiro Kusumi void 17118679ed64STomohiro Kusumi score_printf(size_t i, size_t w, const char *ctl, ...) 17128679ed64STomohiro Kusumi { 17138679ed64STomohiro Kusumi va_list va; 17148679ed64STomohiro Kusumi size_t n; 17158679ed64STomohiro Kusumi static size_t SSize; 17168679ed64STomohiro Kusumi static int SFd = -1; 17178679ed64STomohiro Kusumi static char ScoreBuf[1024]; 17188679ed64STomohiro Kusumi 17198679ed64STomohiro Kusumi if (ScoreBoardFile == NULL) 17208679ed64STomohiro Kusumi return; 17218679ed64STomohiro Kusumi assert(i + w < sizeof(ScoreBuf)); 17228679ed64STomohiro Kusumi if (SFd < 0) { 17238679ed64STomohiro Kusumi SFd = open(ScoreBoardFile, O_RDWR|O_CREAT|O_TRUNC, 0644); 17248679ed64STomohiro Kusumi if (SFd < 0) 17258679ed64STomohiro Kusumi return; 17268679ed64STomohiro Kusumi SSize = 0; 17278679ed64STomohiro Kusumi } 17288679ed64STomohiro Kusumi for (n = 0; n < i; ++n) { 17298679ed64STomohiro Kusumi if (ScoreBuf[n] == 0) 17308679ed64STomohiro Kusumi ScoreBuf[n] = ' '; 17318679ed64STomohiro Kusumi } 17328679ed64STomohiro Kusumi va_start(va, ctl); 17338679ed64STomohiro Kusumi vsnprintf(ScoreBuf + i, w - 1, ctl, va); 17348679ed64STomohiro Kusumi va_end(va); 17358679ed64STomohiro Kusumi n = strlen(ScoreBuf + i); 17368679ed64STomohiro Kusumi while (n < w - 1) { 17378679ed64STomohiro Kusumi ScoreBuf[i + n] = ' '; 17388679ed64STomohiro Kusumi ++n; 17398679ed64STomohiro Kusumi } 17408679ed64STomohiro Kusumi ScoreBuf[i + n] = '\n'; 17418679ed64STomohiro Kusumi if (SSize < i + w) 17428679ed64STomohiro Kusumi SSize = i + w; 17438679ed64STomohiro Kusumi pwrite(SFd, ScoreBuf, SSize, 0); 17448679ed64STomohiro Kusumi } 17458679ed64STomohiro Kusumi 1746*005a4da7STomohiro Kusumi static 1747*005a4da7STomohiro Kusumi void 17488679ed64STomohiro Kusumi hammer_check_restrict(const char *filesystem) 17498679ed64STomohiro Kusumi { 17508679ed64STomohiro Kusumi size_t rlen; 17518679ed64STomohiro Kusumi int atslash; 17528679ed64STomohiro Kusumi 17538679ed64STomohiro Kusumi if (RestrictTarget == NULL) 17548679ed64STomohiro Kusumi return; 17558679ed64STomohiro Kusumi rlen = strlen(RestrictTarget); 1756052fd72bSTomohiro Kusumi if (strncmp(filesystem, RestrictTarget, rlen) != 0) { 175702318f07STomohiro Kusumi errx(1, "hammer-remote: restricted target"); 1758052fd72bSTomohiro Kusumi /* not reached */ 1759052fd72bSTomohiro Kusumi } 176002318f07STomohiro Kusumi 17618679ed64STomohiro Kusumi atslash = 1; 17628679ed64STomohiro Kusumi while (filesystem[rlen]) { 17638679ed64STomohiro Kusumi if (atslash && 17648679ed64STomohiro Kusumi filesystem[rlen] == '.' && 1765f254e677STomohiro Kusumi filesystem[rlen+1] == '.') { 176602318f07STomohiro Kusumi errx(1, "hammer-remote: '..' not allowed"); 1767052fd72bSTomohiro Kusumi /* not reached */ 1768f254e677STomohiro Kusumi } 17698679ed64STomohiro Kusumi if (filesystem[rlen] == '/') 17708679ed64STomohiro Kusumi atslash = 1; 17718679ed64STomohiro Kusumi else 17728679ed64STomohiro Kusumi atslash = 0; 17738679ed64STomohiro Kusumi ++rlen; 17748679ed64STomohiro Kusumi } 17758679ed64STomohiro Kusumi } 17768679ed64STomohiro Kusumi 1777*005a4da7STomohiro Kusumi static 1778*005a4da7STomohiro Kusumi void 1779a7fbbf91SMatthew Dillon mirror_usage(int code) 1780a7fbbf91SMatthew Dillon { 1781a7fbbf91SMatthew Dillon fprintf(stderr, 178234bb69d8SThomas Nikolajsen "hammer mirror-read <filesystem> [begin-tid]\n" 178334bb69d8SThomas Nikolajsen "hammer mirror-read-stream <filesystem> [begin-tid]\n" 1784a7fbbf91SMatthew Dillon "hammer mirror-write <filesystem>\n" 17859f1b0121SAntonio Huete Jimenez "hammer mirror-dump [header]\n" 178634bb69d8SThomas Nikolajsen "hammer mirror-copy [[user@]host:]<filesystem>" 178734bb69d8SThomas Nikolajsen " [[user@]host:]<filesystem>\n" 178834bb69d8SThomas Nikolajsen "hammer mirror-stream [[user@]host:]<filesystem>" 178934bb69d8SThomas Nikolajsen " [[user@]host:]<filesystem>\n" 1790a7fbbf91SMatthew Dillon ); 1791a7fbbf91SMatthew Dillon exit(code); 1792a7fbbf91SMatthew Dillon } 179301a72c9fSMatthew Dillon 1794