1a7fbbf91SMatthew Dillon /* 2a7fbbf91SMatthew Dillon * Copyright (c) 2008 The DragonFly Project. All rights reserved. 3a7fbbf91SMatthew Dillon * 4a7fbbf91SMatthew Dillon * This code is derived from software contributed to The DragonFly Project 5a7fbbf91SMatthew Dillon * by Matthew Dillon <dillon@backplane.com> 6a7fbbf91SMatthew Dillon * 7a7fbbf91SMatthew Dillon * Redistribution and use in source and binary forms, with or without 8a7fbbf91SMatthew Dillon * modification, are permitted provided that the following conditions 9a7fbbf91SMatthew Dillon * are met: 10a7fbbf91SMatthew Dillon * 11a7fbbf91SMatthew Dillon * 1. Redistributions of source code must retain the above copyright 12a7fbbf91SMatthew Dillon * notice, this list of conditions and the following disclaimer. 13a7fbbf91SMatthew Dillon * 2. Redistributions in binary form must reproduce the above copyright 14a7fbbf91SMatthew Dillon * notice, this list of conditions and the following disclaimer in 15a7fbbf91SMatthew Dillon * the documentation and/or other materials provided with the 16a7fbbf91SMatthew Dillon * distribution. 17a7fbbf91SMatthew Dillon * 3. Neither the name of The DragonFly Project nor the names of its 18a7fbbf91SMatthew Dillon * contributors may be used to endorse or promote products derived 19a7fbbf91SMatthew Dillon * from this software without specific, prior written permission. 20a7fbbf91SMatthew Dillon * 21a7fbbf91SMatthew Dillon * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22a7fbbf91SMatthew Dillon * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23a7fbbf91SMatthew Dillon * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24a7fbbf91SMatthew Dillon * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25a7fbbf91SMatthew Dillon * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26a7fbbf91SMatthew Dillon * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27a7fbbf91SMatthew Dillon * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28a7fbbf91SMatthew Dillon * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29a7fbbf91SMatthew Dillon * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30a7fbbf91SMatthew Dillon * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31a7fbbf91SMatthew Dillon * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32a7fbbf91SMatthew Dillon * SUCH DAMAGE. 33a7fbbf91SMatthew Dillon * 3401a72c9fSMatthew Dillon * $DragonFly: src/sbin/hammer/cmd_mirror.c,v 1.16 2008/11/09 05:22:56 dillon Exp $ 35a7fbbf91SMatthew Dillon */ 36a7fbbf91SMatthew Dillon 37a7fbbf91SMatthew Dillon #include "hammer.h" 38a7fbbf91SMatthew Dillon 39a7fbbf91SMatthew Dillon #define SERIALBUF_SIZE (512 * 1024) 40a7fbbf91SMatthew Dillon 41a7fbbf91SMatthew Dillon static int read_mrecords(int fd, char *buf, u_int size, 4217dd83bcSMatthew Dillon hammer_ioc_mrecord_head_t pickup); 43e7f926a5SMatthew Dillon static int generate_histogram(int fd, const char *filesystem, 44e7f926a5SMatthew Dillon hammer_tid_t **histogram_ary, 45e7f926a5SMatthew Dillon struct hammer_ioc_mirror_rw *mirror_base); 4617dd83bcSMatthew Dillon static hammer_ioc_mrecord_any_t read_mrecord(int fdin, int *errorp, 4717dd83bcSMatthew Dillon hammer_ioc_mrecord_head_t pickup); 4817dd83bcSMatthew Dillon static void write_mrecord(int fdout, u_int32_t type, 4917dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec, int bytes); 50e7f926a5SMatthew Dillon static void generate_mrec_header(int fd, int pfs_id, 51e7f926a5SMatthew Dillon union hammer_ioc_mrecord_any *mrec_tmp); 5248eadef9SMatthew Dillon static int validate_mrec_header(int fd, int fdin, int is_target, int pfs_id, 5348eadef9SMatthew Dillon struct hammer_ioc_mrecord_head *pickup, 5434ebae70SMatthew Dillon hammer_tid_t *tid_begp, hammer_tid_t *tid_endp); 55d4e5b69bSMatthew Dillon static void update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id); 5648eadef9SMatthew Dillon static ssize_t writebw(int fd, const void *buf, size_t nbytes, 5748eadef9SMatthew Dillon u_int64_t *bwcount, struct timeval *tv1); 5801a72c9fSMatthew Dillon static int getyn(void); 59a7fbbf91SMatthew Dillon static void mirror_usage(int code); 60a7fbbf91SMatthew Dillon 61e7f926a5SMatthew Dillon #define BULK_MINIMUM 20000 62e7f926a5SMatthew Dillon 6317dd83bcSMatthew Dillon /* 6417dd83bcSMatthew Dillon * Generate a mirroring data stream from the specific source over the 6517dd83bcSMatthew Dillon * entire key range, but restricted to the specified transaction range. 6617dd83bcSMatthew Dillon * 6717dd83bcSMatthew Dillon * The HAMMER VFS does most of the work, we add a few new mrecord 6817dd83bcSMatthew Dillon * types to negotiate the TID ranges and verify that the entire 6917dd83bcSMatthew Dillon * stream made it to the destination. 7017dd83bcSMatthew Dillon */ 71a7fbbf91SMatthew Dillon void 7248eadef9SMatthew Dillon hammer_cmd_mirror_read(char **av, int ac, int streaming) 73a7fbbf91SMatthew Dillon { 74a7fbbf91SMatthew Dillon struct hammer_ioc_mirror_rw mirror; 75d4e5b69bSMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 7617dd83bcSMatthew Dillon union hammer_ioc_mrecord_any mrec_tmp; 7748eadef9SMatthew Dillon struct hammer_ioc_mrecord_head pickup; 7817dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 79243ca327SMatthew Dillon hammer_tid_t sync_tid; 80e7f926a5SMatthew Dillon hammer_tid_t *histogram_ary; 81a7fbbf91SMatthew Dillon const char *filesystem; 82a7fbbf91SMatthew Dillon char *buf = malloc(SERIALBUF_SIZE); 83243ca327SMatthew Dillon int interrupted = 0; 84243ca327SMatthew Dillon int error; 85a7fbbf91SMatthew Dillon int fd; 86243ca327SMatthew Dillon int n; 8748eadef9SMatthew Dillon int didwork; 88e7f926a5SMatthew Dillon int histogram; 8948eadef9SMatthew Dillon int64_t total_bytes; 90243ca327SMatthew Dillon time_t base_t = time(NULL); 9148eadef9SMatthew Dillon struct timeval bwtv; 9248eadef9SMatthew Dillon u_int64_t bwcount; 93a7fbbf91SMatthew Dillon 9434bb69d8SThomas Nikolajsen if (ac == 0 || ac > 2) 95a7fbbf91SMatthew Dillon mirror_usage(1); 96a7fbbf91SMatthew Dillon filesystem = av[0]; 97a7fbbf91SMatthew Dillon 9848eadef9SMatthew Dillon pickup.signature = 0; 9948eadef9SMatthew Dillon pickup.type = 0; 100e7f926a5SMatthew Dillon histogram = -1; 101e7f926a5SMatthew Dillon histogram_ary = NULL; 10248eadef9SMatthew Dillon 10348eadef9SMatthew Dillon again: 104a7fbbf91SMatthew Dillon bzero(&mirror, sizeof(mirror)); 105a7fbbf91SMatthew Dillon hammer_key_beg_init(&mirror.key_beg); 106a7fbbf91SMatthew Dillon hammer_key_end_init(&mirror.key_end); 107a7fbbf91SMatthew Dillon 108d4e5b69bSMatthew Dillon fd = getpfs(&pfs, filesystem); 109a7fbbf91SMatthew Dillon 11048eadef9SMatthew Dillon if (streaming && VerboseOpt) { 11148eadef9SMatthew Dillon fprintf(stderr, "\nRunning"); 11248eadef9SMatthew Dillon fflush(stderr); 11348eadef9SMatthew Dillon } 11448eadef9SMatthew Dillon total_bytes = 0; 11548eadef9SMatthew Dillon gettimeofday(&bwtv, NULL); 11648eadef9SMatthew Dillon bwcount = 0; 11748eadef9SMatthew Dillon 118243ca327SMatthew Dillon /* 119e7f926a5SMatthew Dillon * Send initial header for the purpose of determining the 120e7f926a5SMatthew Dillon * shared-uuid. 12101a72c9fSMatthew Dillon */ 122e7f926a5SMatthew Dillon generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp); 123e7f926a5SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_PFSD, 124e7f926a5SMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.pfs)); 12501a72c9fSMatthew Dillon 12601a72c9fSMatthew Dillon /* 127d4e5b69bSMatthew Dillon * In 2-way mode the target will send us a PFS info packet 128d4e5b69bSMatthew Dillon * first. Use the target's current snapshot TID as our default 129d4e5b69bSMatthew Dillon * begin TID. 130243ca327SMatthew Dillon */ 131d4e5b69bSMatthew Dillon mirror.tid_beg = 0; 13248eadef9SMatthew Dillon if (TwoWayPipeOpt) { 13348eadef9SMatthew Dillon n = validate_mrec_header(fd, 0, 0, pfs.pfs_id, &pickup, 134d4e5b69bSMatthew Dillon NULL, &mirror.tid_beg); 13548eadef9SMatthew Dillon if (n < 0) { /* got TERM record */ 13648eadef9SMatthew Dillon relpfs(fd, &pfs); 13748eadef9SMatthew Dillon return; 13848eadef9SMatthew Dillon } 13948eadef9SMatthew Dillon ++mirror.tid_beg; 14048eadef9SMatthew Dillon } 141d4e5b69bSMatthew Dillon 142d4e5b69bSMatthew Dillon /* 143d4e5b69bSMatthew Dillon * Write out the PFS header, tid_beg will be updated if our PFS 144d4e5b69bSMatthew Dillon * has a larger begin sync. tid_end is set to the latest source 145d4e5b69bSMatthew Dillon * TID whos flush cycle has completed. 146d4e5b69bSMatthew Dillon */ 147e7f926a5SMatthew Dillon generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp); 148e7f926a5SMatthew Dillon if (mirror.tid_beg < mrec_tmp.pfs.pfsd.sync_beg_tid) 149e7f926a5SMatthew Dillon mirror.tid_beg = mrec_tmp.pfs.pfsd.sync_beg_tid; 150e7f926a5SMatthew Dillon mirror.tid_end = mrec_tmp.pfs.pfsd.sync_end_tid; 151e7f926a5SMatthew Dillon mirror.ubuf = buf; 152e7f926a5SMatthew Dillon mirror.size = SERIALBUF_SIZE; 153e7f926a5SMatthew Dillon mirror.pfs_id = pfs.pfs_id; 154e7f926a5SMatthew Dillon mirror.shared_uuid = pfs.ondisk->shared_uuid; 155d4e5b69bSMatthew Dillon 156d4e5b69bSMatthew Dillon /* 157e7f926a5SMatthew Dillon * XXX If the histogram is exhausted and the TID delta is large 158e7f926a5SMatthew Dillon * the stream might have been offline for a while and is 159e7f926a5SMatthew Dillon * now picking it up again. Do another histogram. 160d4e5b69bSMatthew Dillon */ 161e7f926a5SMatthew Dillon #if 0 162e7f926a5SMatthew Dillon if (TwoWayPipeOpt && streaming && histogram == 0) { 163e7f926a5SMatthew Dillon if (mirror.tid_end - mirror.tid_beg > BULK_MINIMUM) 164e7f926a5SMatthew Dillon histogram = -1; 165e7f926a5SMatthew Dillon } 166e7f926a5SMatthew Dillon #endif 16734ebae70SMatthew Dillon 168e7f926a5SMatthew Dillon /* 169e7f926a5SMatthew Dillon * Initial bulk startup control, try to do some incremental 170e7f926a5SMatthew Dillon * mirroring in order to allow the stream to be killed and 171e7f926a5SMatthew Dillon * restarted without having to start over. 172e7f926a5SMatthew Dillon */ 173*0bd7a37cSMatthew Dillon if (histogram < 0 && BulkOpt == 0) { 174e7f926a5SMatthew Dillon if (VerboseOpt) 175e7f926a5SMatthew Dillon fprintf(stderr, "\n"); 176e7f926a5SMatthew Dillon histogram = generate_histogram(fd, filesystem, 177e7f926a5SMatthew Dillon &histogram_ary, &mirror); 178e7f926a5SMatthew Dillon } 179e7f926a5SMatthew Dillon 180e7f926a5SMatthew Dillon if (TwoWayPipeOpt && streaming && histogram > 0) { 181e7f926a5SMatthew Dillon mirror.tid_end = histogram_ary[--histogram]; 182e7f926a5SMatthew Dillon mrec_tmp.pfs.pfsd.sync_end_tid = mirror.tid_end; 183e7f926a5SMatthew Dillon } 184e7f926a5SMatthew Dillon 185e7f926a5SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_PFSD, 186e7f926a5SMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.pfs)); 187e7f926a5SMatthew Dillon 188e7f926a5SMatthew Dillon /* 189e7f926a5SMatthew Dillon * A cycle file overrides the beginning TID only if we are 190e7f926a5SMatthew Dillon * not operating in two-way mode. 191e7f926a5SMatthew Dillon */ 192e7f926a5SMatthew Dillon if (TwoWayPipeOpt == 0) { 193e7f926a5SMatthew Dillon hammer_get_cycle(&mirror.key_beg, &mirror.tid_beg); 194e7f926a5SMatthew Dillon } 195e7f926a5SMatthew Dillon 196e7f926a5SMatthew Dillon /* 197e7f926a5SMatthew Dillon * An additional argument overrides the beginning TID regardless 198e7f926a5SMatthew Dillon * of what mode we are in. This is not recommending if operating 199e7f926a5SMatthew Dillon * in two-way mode. 200e7f926a5SMatthew Dillon */ 20117dd83bcSMatthew Dillon if (ac == 2) 20217dd83bcSMatthew Dillon mirror.tid_beg = strtoull(av[1], NULL, 0); 20317dd83bcSMatthew Dillon 20448eadef9SMatthew Dillon if (streaming == 0 || VerboseOpt >= 2) { 20548eadef9SMatthew Dillon fprintf(stderr, 20648eadef9SMatthew Dillon "Mirror-read: Mirror from %016llx to %016llx\n", 207243ca327SMatthew Dillon mirror.tid_beg, mirror.tid_end); 20848eadef9SMatthew Dillon } 209243ca327SMatthew Dillon if (mirror.key_beg.obj_id != (int64_t)HAMMER_MIN_OBJID) { 2109dc76cb1SMatthew Dillon fprintf(stderr, "Mirror-read: Resuming at object %016llx\n", 211243ca327SMatthew Dillon mirror.key_beg.obj_id); 212243ca327SMatthew Dillon } 213243ca327SMatthew Dillon 214243ca327SMatthew Dillon /* 2159dc76cb1SMatthew Dillon * Nothing to do if begin equals end. 2169dc76cb1SMatthew Dillon */ 21748eadef9SMatthew Dillon if (mirror.tid_beg >= mirror.tid_end) { 21848eadef9SMatthew Dillon if (streaming == 0 || VerboseOpt >= 2) 21948eadef9SMatthew Dillon fprintf(stderr, "Mirror-read: No work to do\n"); 22048eadef9SMatthew Dillon didwork = 0; 2219dc76cb1SMatthew Dillon goto done; 2229dc76cb1SMatthew Dillon } 22348eadef9SMatthew Dillon didwork = 1; 2249dc76cb1SMatthew Dillon 2259dc76cb1SMatthew Dillon /* 226243ca327SMatthew Dillon * Write out bulk records 227243ca327SMatthew Dillon */ 228a7fbbf91SMatthew Dillon mirror.ubuf = buf; 229a7fbbf91SMatthew Dillon mirror.size = SERIALBUF_SIZE; 230a7fbbf91SMatthew Dillon 231a7fbbf91SMatthew Dillon do { 232a7fbbf91SMatthew Dillon mirror.count = 0; 233d4e5b69bSMatthew Dillon mirror.pfs_id = pfs.pfs_id; 234d4e5b69bSMatthew Dillon mirror.shared_uuid = pfs.ondisk->shared_uuid; 235a7fbbf91SMatthew Dillon if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) { 236a7fbbf91SMatthew Dillon fprintf(stderr, "Mirror-read %s failed: %s\n", 237a7fbbf91SMatthew Dillon filesystem, strerror(errno)); 238a7fbbf91SMatthew Dillon exit(1); 239a7fbbf91SMatthew Dillon } 2409c67b4d2SMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) { 2419c67b4d2SMatthew Dillon fprintf(stderr, 2429c67b4d2SMatthew Dillon "Mirror-read %s fatal error %d\n", 2439c67b4d2SMatthew Dillon filesystem, mirror.head.error); 2449c67b4d2SMatthew Dillon exit(1); 2459c67b4d2SMatthew Dillon } 246243ca327SMatthew Dillon if (mirror.count) { 24748eadef9SMatthew Dillon if (BandwidthOpt) { 24848eadef9SMatthew Dillon n = writebw(1, mirror.ubuf, mirror.count, 24948eadef9SMatthew Dillon &bwcount, &bwtv); 25048eadef9SMatthew Dillon } else { 251243ca327SMatthew Dillon n = write(1, mirror.ubuf, mirror.count); 25248eadef9SMatthew Dillon } 253243ca327SMatthew Dillon if (n != mirror.count) { 254243ca327SMatthew Dillon fprintf(stderr, "Mirror-read %s failed: " 255243ca327SMatthew Dillon "short write\n", 256243ca327SMatthew Dillon filesystem); 257243ca327SMatthew Dillon exit(1); 258243ca327SMatthew Dillon } 259a7fbbf91SMatthew Dillon } 26048eadef9SMatthew Dillon total_bytes += mirror.count; 26148eadef9SMatthew Dillon if (streaming && VerboseOpt) { 262e7f926a5SMatthew Dillon fprintf(stderr, 263e7f926a5SMatthew Dillon "\robj=%016llx tids=%016llx:%016llx %11lld", 264e7f926a5SMatthew Dillon (long long)mirror.key_cur.obj_id, 265e7f926a5SMatthew Dillon (long long)mirror.tid_beg, 266e7f926a5SMatthew Dillon (long long)mirror.tid_end, 26748eadef9SMatthew Dillon total_bytes); 26848eadef9SMatthew Dillon fflush(stderr); 26948eadef9SMatthew Dillon } 270a7fbbf91SMatthew Dillon mirror.key_beg = mirror.key_cur; 271e7f926a5SMatthew Dillon 272e7f926a5SMatthew Dillon /* 273e7f926a5SMatthew Dillon * Deal with time limit option 274e7f926a5SMatthew Dillon */ 275243ca327SMatthew Dillon if (TimeoutOpt && 276243ca327SMatthew Dillon (unsigned)(time(NULL) - base_t) > (unsigned)TimeoutOpt) { 277243ca327SMatthew Dillon fprintf(stderr, 278243ca327SMatthew Dillon "Mirror-read %s interrupted by timer at" 279243ca327SMatthew Dillon " %016llx\n", 280243ca327SMatthew Dillon filesystem, 281243ca327SMatthew Dillon mirror.key_cur.obj_id); 282243ca327SMatthew Dillon interrupted = 1; 283243ca327SMatthew Dillon break; 284243ca327SMatthew Dillon } 285a7fbbf91SMatthew Dillon } while (mirror.count != 0); 286a7fbbf91SMatthew Dillon 28748eadef9SMatthew Dillon done: 288243ca327SMatthew Dillon /* 28948eadef9SMatthew Dillon * Write out the termination sync record - only if not interrupted 290243ca327SMatthew Dillon */ 29148eadef9SMatthew Dillon if (interrupted == 0) { 29248eadef9SMatthew Dillon if (didwork) { 29317dd83bcSMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_SYNC, 29417dd83bcSMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.sync)); 29548eadef9SMatthew Dillon } else { 29648eadef9SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_IDLE, 29748eadef9SMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.sync)); 29848eadef9SMatthew Dillon } 29948eadef9SMatthew Dillon } 30034ebae70SMatthew Dillon 301243ca327SMatthew Dillon /* 302243ca327SMatthew Dillon * If the -2 option was given (automatic when doing mirror-copy), 303243ca327SMatthew Dillon * a two-way pipe is assumed and we expect a response mrec from 304243ca327SMatthew Dillon * the target. 305243ca327SMatthew Dillon */ 306243ca327SMatthew Dillon if (TwoWayPipeOpt) { 30748eadef9SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 30817dd83bcSMatthew Dillon if (mrec == NULL || 30917dd83bcSMatthew Dillon mrec->head.type != HAMMER_MREC_TYPE_UPDATE || 31017dd83bcSMatthew Dillon mrec->head.rec_size != sizeof(mrec->update)) { 311243ca327SMatthew Dillon fprintf(stderr, "mirror_read: Did not get final " 312243ca327SMatthew Dillon "acknowledgement packet from target\n"); 313243ca327SMatthew Dillon exit(1); 314243ca327SMatthew Dillon } 315243ca327SMatthew Dillon if (interrupted) { 316243ca327SMatthew Dillon if (CyclePath) { 317243ca327SMatthew Dillon hammer_set_cycle(&mirror.key_cur, mirror.tid_beg); 31834bb69d8SThomas Nikolajsen fprintf(stderr, "Cyclefile %s updated for " 31934bb69d8SThomas Nikolajsen "continuation\n", CyclePath); 320243ca327SMatthew Dillon } 321243ca327SMatthew Dillon } else { 32217dd83bcSMatthew Dillon sync_tid = mrec->update.tid; 323243ca327SMatthew Dillon if (CyclePath) { 324243ca327SMatthew Dillon hammer_key_beg_init(&mirror.key_beg); 325243ca327SMatthew Dillon hammer_set_cycle(&mirror.key_beg, sync_tid); 326243ca327SMatthew Dillon fprintf(stderr, "Cyclefile %s updated to 0x%016llx\n", 327243ca327SMatthew Dillon CyclePath, sync_tid); 328243ca327SMatthew Dillon } 329243ca327SMatthew Dillon } 330243ca327SMatthew Dillon } else if (CyclePath) { 331243ca327SMatthew Dillon /* NOTE! mirror.tid_beg cannot be updated */ 332243ca327SMatthew Dillon fprintf(stderr, "Warning: cycle file (-c option) cannot be " 333243ca327SMatthew Dillon "fully updated unless you use mirror-copy\n"); 334243ca327SMatthew Dillon hammer_set_cycle(&mirror.key_beg, mirror.tid_beg); 335243ca327SMatthew Dillon } 33648eadef9SMatthew Dillon if (streaming && interrupted == 0) { 33748eadef9SMatthew Dillon time_t t1 = time(NULL); 33848eadef9SMatthew Dillon time_t t2; 33948eadef9SMatthew Dillon 340e7f926a5SMatthew Dillon /* 341e7f926a5SMatthew Dillon * Two way streaming tries to break down large bulk 342e7f926a5SMatthew Dillon * transfers into smaller ones so it can sync the 343e7f926a5SMatthew Dillon * transaction id on the slave. This way if we get 344e7f926a5SMatthew Dillon * interrupted a restart doesn't have to start from 345e7f926a5SMatthew Dillon * scratch. 346e7f926a5SMatthew Dillon */ 347e7f926a5SMatthew Dillon if (TwoWayPipeOpt && streaming && histogram > 0) { 348e7f926a5SMatthew Dillon if (VerboseOpt) 349e7f926a5SMatthew Dillon fprintf(stderr, " (bulk incremental)"); 350e7f926a5SMatthew Dillon goto again; 351e7f926a5SMatthew Dillon } 352e7f926a5SMatthew Dillon 35348eadef9SMatthew Dillon if (VerboseOpt) { 35448eadef9SMatthew Dillon fprintf(stderr, " W"); 35548eadef9SMatthew Dillon fflush(stderr); 35648eadef9SMatthew Dillon } 35748eadef9SMatthew Dillon pfs.ondisk->sync_end_tid = mirror.tid_end; 35848eadef9SMatthew Dillon if (ioctl(fd, HAMMERIOC_WAI_PSEUDOFS, &pfs) < 0) { 35948eadef9SMatthew Dillon fprintf(stderr, "Mirror-read %s: cannot stream: %s\n", 36048eadef9SMatthew Dillon filesystem, strerror(errno)); 36148eadef9SMatthew Dillon } else { 36248eadef9SMatthew Dillon t2 = time(NULL) - t1; 36348eadef9SMatthew Dillon if (t2 >= 0 && t2 < DelayOpt) { 36448eadef9SMatthew Dillon if (VerboseOpt) { 36548eadef9SMatthew Dillon fprintf(stderr, "\bD"); 36648eadef9SMatthew Dillon fflush(stderr); 36748eadef9SMatthew Dillon } 36848eadef9SMatthew Dillon sleep(DelayOpt - t2); 36948eadef9SMatthew Dillon } 37048eadef9SMatthew Dillon if (VerboseOpt) { 37148eadef9SMatthew Dillon fprintf(stderr, "\b "); 37248eadef9SMatthew Dillon fflush(stderr); 37348eadef9SMatthew Dillon } 37448eadef9SMatthew Dillon relpfs(fd, &pfs); 37548eadef9SMatthew Dillon goto again; 37648eadef9SMatthew Dillon } 37748eadef9SMatthew Dillon } 37848eadef9SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_TERM, 37948eadef9SMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.sync)); 38048eadef9SMatthew Dillon relpfs(fd, &pfs); 381a7fbbf91SMatthew Dillon fprintf(stderr, "Mirror-read %s succeeded\n", filesystem); 382a7fbbf91SMatthew Dillon } 383a7fbbf91SMatthew Dillon 384e7f926a5SMatthew Dillon /* 385e7f926a5SMatthew Dillon * Ok, this isn't really a histogram. What we are trying to do 386e7f926a5SMatthew Dillon * here is find the first tid_end for the scan that returns 387e7f926a5SMatthew Dillon * at least some data. The frontend of the TID space will generally 388e7f926a5SMatthew Dillon * return nothing so we can't just divide out the full mirroring 389e7f926a5SMatthew Dillon * range. Once we find the point where a real data stream starts 390e7f926a5SMatthew Dillon * to get generated we can divide out the range from that point. 391e7f926a5SMatthew Dillon * 392e7f926a5SMatthew Dillon * When starting a new mirroring operation completely from scratch 393e7f926a5SMatthew Dillon * this code will take some time to run, but once some mirroring 394e7f926a5SMatthew Dillon * data is synchronized on the target you will be able to interrupt 395e7f926a5SMatthew Dillon * the stream and restart it and the later invocations of this 396e7f926a5SMatthew Dillon * code will be such that it should run much faster. 397e7f926a5SMatthew Dillon */ 398e7f926a5SMatthew Dillon static int 399e7f926a5SMatthew Dillon generate_histogram(int fd, const char *filesystem, 400e7f926a5SMatthew Dillon hammer_tid_t **histogram_ary, 401e7f926a5SMatthew Dillon struct hammer_ioc_mirror_rw *mirror_base) 402e7f926a5SMatthew Dillon { 403e7f926a5SMatthew Dillon struct hammer_ioc_mirror_rw mirror; 404e7f926a5SMatthew Dillon hammer_tid_t tid_beg; 405e7f926a5SMatthew Dillon hammer_tid_t tid_end; 406e7f926a5SMatthew Dillon hammer_tid_t tid_half; 407e7f926a5SMatthew Dillon int i; 408e7f926a5SMatthew Dillon 409e7f926a5SMatthew Dillon mirror = *mirror_base; 410e7f926a5SMatthew Dillon tid_beg = mirror.tid_beg; 411e7f926a5SMatthew Dillon tid_end = mirror.tid_end; 412e7f926a5SMatthew Dillon 413e7f926a5SMatthew Dillon if (*histogram_ary) 414e7f926a5SMatthew Dillon free(*histogram_ary); 415e7f926a5SMatthew Dillon if (tid_beg + BULK_MINIMUM >= tid_end) 416e7f926a5SMatthew Dillon return(0); 417e7f926a5SMatthew Dillon 418e7f926a5SMatthew Dillon if (VerboseOpt) 419e7f926a5SMatthew Dillon fprintf(stderr, "Doing Range Test\n"); 420e7f926a5SMatthew Dillon while (tid_end - tid_beg > BULK_MINIMUM) { 421e7f926a5SMatthew Dillon tid_half = tid_beg + (tid_end - tid_beg) * 2 / 3; 422e7f926a5SMatthew Dillon mirror.count = 0; 423e7f926a5SMatthew Dillon mirror.tid_beg = tid_beg; 424e7f926a5SMatthew Dillon mirror.tid_end = tid_half; 425e7f926a5SMatthew Dillon 426e7f926a5SMatthew Dillon if (VerboseOpt > 1) { 427e7f926a5SMatthew Dillon fprintf(stderr, "RangeTest %016llx/%016llx - %016llx (%lld) ", 428e7f926a5SMatthew Dillon (long long)tid_beg, 429e7f926a5SMatthew Dillon (long long)tid_end, 430e7f926a5SMatthew Dillon (long long)tid_half, 431e7f926a5SMatthew Dillon (long long)(tid_half - tid_beg)); 432e7f926a5SMatthew Dillon } 433e7f926a5SMatthew Dillon fflush(stderr); 434e7f926a5SMatthew Dillon if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) { 435e7f926a5SMatthew Dillon fprintf(stderr, "Mirror-read %s failed: %s\n", 436e7f926a5SMatthew Dillon filesystem, strerror(errno)); 437e7f926a5SMatthew Dillon exit(1); 438e7f926a5SMatthew Dillon } 439e7f926a5SMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) { 440e7f926a5SMatthew Dillon fprintf(stderr, 441e7f926a5SMatthew Dillon "Mirror-read %s fatal error %d\n", 442e7f926a5SMatthew Dillon filesystem, mirror.head.error); 443e7f926a5SMatthew Dillon exit(1); 444e7f926a5SMatthew Dillon } 445e7f926a5SMatthew Dillon if (VerboseOpt > 1) 446e7f926a5SMatthew Dillon fprintf(stderr, "%d\n", mirror.count); 447e7f926a5SMatthew Dillon if (mirror.count > SERIALBUF_SIZE / 2) { 448e7f926a5SMatthew Dillon tid_end = tid_half; 449e7f926a5SMatthew Dillon } else { 450e7f926a5SMatthew Dillon tid_beg = tid_half; 451e7f926a5SMatthew Dillon } 452e7f926a5SMatthew Dillon } 453e7f926a5SMatthew Dillon 454e7f926a5SMatthew Dillon tid_end = mirror_base->tid_end; 455e7f926a5SMatthew Dillon fprintf(stderr, "histogram range %016llx - %016llx\n", 456e7f926a5SMatthew Dillon (long long)tid_beg, (long long)tid_end); 457e7f926a5SMatthew Dillon 458e7f926a5SMatthew Dillon /* 459e7f926a5SMatthew Dillon * The final array generates our incremental ending tids in 460e7f926a5SMatthew Dillon * reverse order. The caller also picks them off in reverse order. 461e7f926a5SMatthew Dillon */ 462e7f926a5SMatthew Dillon *histogram_ary = malloc(sizeof(hammer_tid_t) * 20); 463e7f926a5SMatthew Dillon for (i = 0; i < 20; ++i) { 464e7f926a5SMatthew Dillon (*histogram_ary)[i] = tid_end - (tid_end - tid_beg) / 20 * i; 465e7f926a5SMatthew Dillon } 466e7f926a5SMatthew Dillon return(20); 467e7f926a5SMatthew Dillon } 468e7f926a5SMatthew Dillon 46901a72c9fSMatthew Dillon static void 47001a72c9fSMatthew Dillon create_pfs(const char *filesystem, uuid_t *s_uuid) 47101a72c9fSMatthew Dillon { 472f414d101SSascha Wildner if (ForceYesOpt == 1) { 47307485271SMichael Neumann fprintf(stderr, "PFS slave %s does not exist. " 47407485271SMichael Neumann "Auto create new slave PFS!\n", filesystem); 47507485271SMichael Neumann 476f414d101SSascha Wildner } else { 47701a72c9fSMatthew Dillon fprintf(stderr, "PFS slave %s does not exist.\n" 47801a72c9fSMatthew Dillon "Do you want to create a new slave PFS? (yes|no) ", 47901a72c9fSMatthew Dillon filesystem); 48001a72c9fSMatthew Dillon fflush(stderr); 48101a72c9fSMatthew Dillon if (getyn() != 1) { 48201a72c9fSMatthew Dillon fprintf(stderr, "Aborting operation\n"); 48301a72c9fSMatthew Dillon exit(1); 48401a72c9fSMatthew Dillon } 48507485271SMichael Neumann } 48601a72c9fSMatthew Dillon 48701a72c9fSMatthew Dillon u_int32_t status; 48801a72c9fSMatthew Dillon char *shared_uuid = NULL; 48901a72c9fSMatthew Dillon uuid_to_string(s_uuid, &shared_uuid, &status); 49001a72c9fSMatthew Dillon 49101a72c9fSMatthew Dillon char *cmd = NULL; 49201a72c9fSMatthew Dillon asprintf(&cmd, "/sbin/hammer pfs-slave '%s' shared-uuid=%s 1>&2", 49301a72c9fSMatthew Dillon filesystem, shared_uuid); 49401a72c9fSMatthew Dillon free(shared_uuid); 49501a72c9fSMatthew Dillon 49601a72c9fSMatthew Dillon if (cmd == NULL) { 49701a72c9fSMatthew Dillon fprintf(stderr, "Failed to alloc memory\n"); 49801a72c9fSMatthew Dillon exit(1); 49901a72c9fSMatthew Dillon } 50001a72c9fSMatthew Dillon if (system(cmd) != 0) { 50101a72c9fSMatthew Dillon fprintf(stderr, "Failed to create PFS\n"); 50201a72c9fSMatthew Dillon } 50301a72c9fSMatthew Dillon free(cmd); 50401a72c9fSMatthew Dillon } 50501a72c9fSMatthew Dillon 50617dd83bcSMatthew Dillon /* 50717dd83bcSMatthew Dillon * Pipe the mirroring data stream on stdin to the HAMMER VFS, adding 50817dd83bcSMatthew Dillon * some additional packet types to negotiate TID ranges and to verify 50917dd83bcSMatthew Dillon * completion. The HAMMER VFS does most of the work. 51017dd83bcSMatthew Dillon * 51117dd83bcSMatthew Dillon * It is important to note that the mirror.key_{beg,end} range must 51217dd83bcSMatthew Dillon * match the ranged used by the original. For now both sides use 51317dd83bcSMatthew Dillon * range the entire key space. 51417dd83bcSMatthew Dillon * 51517dd83bcSMatthew Dillon * It is even more important that the records in the stream conform 51617dd83bcSMatthew Dillon * to the TID range also supplied in the stream. The HAMMER VFS will 51717dd83bcSMatthew Dillon * use the REC, PASS, and SKIP record types to track the portions of 51817dd83bcSMatthew Dillon * the B-Tree being scanned in order to be able to proactively delete 51917dd83bcSMatthew Dillon * records on the target within those active areas that are not mentioned 52017dd83bcSMatthew Dillon * by the source. 52117dd83bcSMatthew Dillon * 52217dd83bcSMatthew Dillon * The mirror.key_cur field is used by the VFS to do this tracking. It 52317dd83bcSMatthew Dillon * must be initialized to key_beg but then is persistently updated by 52417dd83bcSMatthew Dillon * the HAMMER VFS on each successive ioctl() call. If you blow up this 52517dd83bcSMatthew Dillon * field you will blow up the mirror target, possibly to the point of 52617dd83bcSMatthew Dillon * deleting everything. As a safety measure the HAMMER VFS simply marks 52717dd83bcSMatthew Dillon * the records that the source has destroyed as deleted on the target, 52817dd83bcSMatthew Dillon * and normal pruning operations will deal with their final disposition 52917dd83bcSMatthew Dillon * at some later time. 53017dd83bcSMatthew Dillon */ 531a7fbbf91SMatthew Dillon void 532a7fbbf91SMatthew Dillon hammer_cmd_mirror_write(char **av, int ac) 533a7fbbf91SMatthew Dillon { 534a7fbbf91SMatthew Dillon struct hammer_ioc_mirror_rw mirror; 535a7fbbf91SMatthew Dillon const char *filesystem; 536a7fbbf91SMatthew Dillon char *buf = malloc(SERIALBUF_SIZE); 537d4e5b69bSMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 53817dd83bcSMatthew Dillon struct hammer_ioc_mrecord_head pickup; 539243ca327SMatthew Dillon struct hammer_ioc_synctid synctid; 54017dd83bcSMatthew Dillon union hammer_ioc_mrecord_any mrec_tmp; 54117dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 54201a72c9fSMatthew Dillon struct stat st; 543243ca327SMatthew Dillon int error; 544243ca327SMatthew Dillon int fd; 54548eadef9SMatthew Dillon int n; 546a7fbbf91SMatthew Dillon 54734bb69d8SThomas Nikolajsen if (ac != 1) 548a7fbbf91SMatthew Dillon mirror_usage(1); 549a7fbbf91SMatthew Dillon filesystem = av[0]; 550a7fbbf91SMatthew Dillon 55148eadef9SMatthew Dillon pickup.signature = 0; 55248eadef9SMatthew Dillon pickup.type = 0; 55348eadef9SMatthew Dillon 55448eadef9SMatthew Dillon again: 555a7fbbf91SMatthew Dillon bzero(&mirror, sizeof(mirror)); 556a7fbbf91SMatthew Dillon hammer_key_beg_init(&mirror.key_beg); 557a7fbbf91SMatthew Dillon hammer_key_end_init(&mirror.key_end); 55817dd83bcSMatthew Dillon mirror.key_end = mirror.key_beg; 559a7fbbf91SMatthew Dillon 56001a72c9fSMatthew Dillon /* 56101a72c9fSMatthew Dillon * Read initial packet 56201a72c9fSMatthew Dillon */ 56301a72c9fSMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 56401a72c9fSMatthew Dillon if (mrec == NULL) { 56501a72c9fSMatthew Dillon if (error == 0) 56601a72c9fSMatthew Dillon fprintf(stderr, "validate_mrec_header: short read\n"); 56701a72c9fSMatthew Dillon exit(1); 56801a72c9fSMatthew Dillon } 56901a72c9fSMatthew Dillon /* 57001a72c9fSMatthew Dillon * Validate packet 57101a72c9fSMatthew Dillon */ 57201a72c9fSMatthew Dillon if (mrec->head.type == HAMMER_MREC_TYPE_TERM) { 57301a72c9fSMatthew Dillon return; 57401a72c9fSMatthew Dillon } 57501a72c9fSMatthew Dillon if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) { 57601a72c9fSMatthew Dillon fprintf(stderr, "validate_mrec_header: did not get expected " 57701a72c9fSMatthew Dillon "PFSD record type\n"); 57801a72c9fSMatthew Dillon exit(1); 57901a72c9fSMatthew Dillon } 58001a72c9fSMatthew Dillon if (mrec->head.rec_size != sizeof(mrec->pfs)) { 58101a72c9fSMatthew Dillon fprintf(stderr, "validate_mrec_header: unexpected payload " 58201a72c9fSMatthew Dillon "size\n"); 58301a72c9fSMatthew Dillon exit(1); 58401a72c9fSMatthew Dillon } 58501a72c9fSMatthew Dillon 58601a72c9fSMatthew Dillon /* 58701a72c9fSMatthew Dillon * Create slave PFS if it doesn't yet exist 58801a72c9fSMatthew Dillon */ 58901a72c9fSMatthew Dillon if (lstat(filesystem, &st) != 0) { 59001a72c9fSMatthew Dillon create_pfs(filesystem, &mrec->pfs.pfsd.shared_uuid); 59101a72c9fSMatthew Dillon } 59201a72c9fSMatthew Dillon free(mrec); 59301a72c9fSMatthew Dillon mrec = NULL; 59401a72c9fSMatthew Dillon 595d4e5b69bSMatthew Dillon fd = getpfs(&pfs, filesystem); 596a7fbbf91SMatthew Dillon 597243ca327SMatthew Dillon /* 598d4e5b69bSMatthew Dillon * In two-way mode the target writes out a PFS packet first. 599d4e5b69bSMatthew Dillon * The source uses our tid_end as its tid_beg by default, 600d4e5b69bSMatthew Dillon * picking up where it left off. 601243ca327SMatthew Dillon */ 602d4e5b69bSMatthew Dillon mirror.tid_beg = 0; 603d4e5b69bSMatthew Dillon if (TwoWayPipeOpt) { 604e7f926a5SMatthew Dillon generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp); 605e7f926a5SMatthew Dillon if (mirror.tid_beg < mrec_tmp.pfs.pfsd.sync_beg_tid) 606e7f926a5SMatthew Dillon mirror.tid_beg = mrec_tmp.pfs.pfsd.sync_beg_tid; 607e7f926a5SMatthew Dillon mirror.tid_end = mrec_tmp.pfs.pfsd.sync_end_tid; 608e7f926a5SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_PFSD, 609e7f926a5SMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.pfs)); 610d4e5b69bSMatthew Dillon } 611d4e5b69bSMatthew Dillon 612d4e5b69bSMatthew Dillon /* 61317dd83bcSMatthew Dillon * Read and process the PFS header. The source informs us of 61417dd83bcSMatthew Dillon * the TID range the stream represents. 615d4e5b69bSMatthew Dillon */ 61648eadef9SMatthew Dillon n = validate_mrec_header(fd, 0, 1, pfs.pfs_id, &pickup, 617d4e5b69bSMatthew Dillon &mirror.tid_beg, &mirror.tid_end); 61848eadef9SMatthew Dillon if (n < 0) { /* got TERM record */ 61948eadef9SMatthew Dillon relpfs(fd, &pfs); 62048eadef9SMatthew Dillon return; 62148eadef9SMatthew Dillon } 62234ebae70SMatthew Dillon 623a7fbbf91SMatthew Dillon mirror.ubuf = buf; 624a7fbbf91SMatthew Dillon mirror.size = SERIALBUF_SIZE; 625a7fbbf91SMatthew Dillon 626243ca327SMatthew Dillon /* 62717dd83bcSMatthew Dillon * Read and process bulk records (REC, PASS, and SKIP types). 62817dd83bcSMatthew Dillon * 62917dd83bcSMatthew Dillon * On your life, do NOT mess with mirror.key_cur or your mirror 63017dd83bcSMatthew Dillon * target may become history. 631243ca327SMatthew Dillon */ 632a7fbbf91SMatthew Dillon for (;;) { 633a7fbbf91SMatthew Dillon mirror.count = 0; 634d4e5b69bSMatthew Dillon mirror.pfs_id = pfs.pfs_id; 635d4e5b69bSMatthew Dillon mirror.shared_uuid = pfs.ondisk->shared_uuid; 636a7fbbf91SMatthew Dillon mirror.size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup); 637a7fbbf91SMatthew Dillon if (mirror.size <= 0) 638a7fbbf91SMatthew Dillon break; 639a7fbbf91SMatthew Dillon if (ioctl(fd, HAMMERIOC_MIRROR_WRITE, &mirror) < 0) { 640a7fbbf91SMatthew Dillon fprintf(stderr, "Mirror-write %s failed: %s\n", 641a7fbbf91SMatthew Dillon filesystem, strerror(errno)); 642a7fbbf91SMatthew Dillon exit(1); 643a7fbbf91SMatthew Dillon } 64417dd83bcSMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) { 64517dd83bcSMatthew Dillon fprintf(stderr, 64617dd83bcSMatthew Dillon "Mirror-write %s fatal error %d\n", 64717dd83bcSMatthew Dillon filesystem, mirror.head.error); 64817dd83bcSMatthew Dillon exit(1); 64917dd83bcSMatthew Dillon } 650243ca327SMatthew Dillon #if 0 651a7fbbf91SMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_INTR) { 652a7fbbf91SMatthew Dillon fprintf(stderr, 653a7fbbf91SMatthew Dillon "Mirror-write %s interrupted by timer at" 654243ca327SMatthew Dillon " %016llx\n", 655a7fbbf91SMatthew Dillon filesystem, 656243ca327SMatthew Dillon mirror.key_cur.obj_id); 657a7fbbf91SMatthew Dillon exit(0); 658a7fbbf91SMatthew Dillon } 659243ca327SMatthew Dillon #endif 660a7fbbf91SMatthew Dillon } 661243ca327SMatthew Dillon 662243ca327SMatthew Dillon /* 663243ca327SMatthew Dillon * Read and process the termination sync record. 664243ca327SMatthew Dillon */ 665243ca327SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 6669dc76cb1SMatthew Dillon 6679dc76cb1SMatthew Dillon if (mrec && mrec->head.type == HAMMER_MREC_TYPE_TERM) { 66848eadef9SMatthew Dillon fprintf(stderr, "Mirror-write: received termination request\n"); 66948eadef9SMatthew Dillon free(mrec); 6709dc76cb1SMatthew Dillon return; 6719dc76cb1SMatthew Dillon } 6729dc76cb1SMatthew Dillon 67317dd83bcSMatthew Dillon if (mrec == NULL || 67448eadef9SMatthew Dillon (mrec->head.type != HAMMER_MREC_TYPE_SYNC && 67548eadef9SMatthew Dillon mrec->head.type != HAMMER_MREC_TYPE_IDLE) || 67617dd83bcSMatthew Dillon mrec->head.rec_size != sizeof(mrec->sync)) { 677243ca327SMatthew Dillon fprintf(stderr, "Mirror-write %s: Did not get termination " 67817dd83bcSMatthew Dillon "sync record, or rec_size is wrong rt=%d\n", 67917dd83bcSMatthew Dillon filesystem, mrec->head.type); 680da44aa75SMatthew Dillon exit(1); 681243ca327SMatthew Dillon } 682243ca327SMatthew Dillon 683243ca327SMatthew Dillon /* 684243ca327SMatthew Dillon * Update the PFS info on the target so the user has visibility 68548eadef9SMatthew Dillon * into the new snapshot, and sync the target filesystem. 686243ca327SMatthew Dillon */ 68748eadef9SMatthew Dillon if (mrec->head.type == HAMMER_MREC_TYPE_SYNC) { 688d4e5b69bSMatthew Dillon update_pfs_snapshot(fd, mirror.tid_end, pfs.pfs_id); 689243ca327SMatthew Dillon 690243ca327SMatthew Dillon bzero(&synctid, sizeof(synctid)); 691243ca327SMatthew Dillon synctid.op = HAMMER_SYNCTID_SYNC2; 692243ca327SMatthew Dillon ioctl(fd, HAMMERIOC_SYNCTID, &synctid); 693243ca327SMatthew Dillon 69448eadef9SMatthew Dillon if (VerboseOpt >= 2) { 69548eadef9SMatthew Dillon fprintf(stderr, "Mirror-write %s: succeeded\n", 69648eadef9SMatthew Dillon filesystem); 69748eadef9SMatthew Dillon } 69848eadef9SMatthew Dillon } 69948eadef9SMatthew Dillon 70048eadef9SMatthew Dillon free(mrec); 70148eadef9SMatthew Dillon mrec = NULL; 702243ca327SMatthew Dillon 703243ca327SMatthew Dillon /* 704243ca327SMatthew Dillon * Report back to the originator. 705243ca327SMatthew Dillon */ 706243ca327SMatthew Dillon if (TwoWayPipeOpt) { 70717dd83bcSMatthew Dillon mrec_tmp.update.tid = mirror.tid_end; 708243ca327SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_UPDATE, 70917dd83bcSMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.update)); 710243ca327SMatthew Dillon } else { 711243ca327SMatthew Dillon printf("Source can update synctid to 0x%016llx\n", 712243ca327SMatthew Dillon mirror.tid_end); 713243ca327SMatthew Dillon } 71448eadef9SMatthew Dillon relpfs(fd, &pfs); 71548eadef9SMatthew Dillon goto again; 716243ca327SMatthew Dillon } 717243ca327SMatthew Dillon 718243ca327SMatthew Dillon void 719243ca327SMatthew Dillon hammer_cmd_mirror_dump(void) 720243ca327SMatthew Dillon { 721243ca327SMatthew Dillon char *buf = malloc(SERIALBUF_SIZE); 72217dd83bcSMatthew Dillon struct hammer_ioc_mrecord_head pickup; 72317dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 724243ca327SMatthew Dillon int error; 725243ca327SMatthew Dillon int size; 72617dd83bcSMatthew Dillon int offset; 72717dd83bcSMatthew Dillon int bytes; 728243ca327SMatthew Dillon 729243ca327SMatthew Dillon /* 730243ca327SMatthew Dillon * Read and process the PFS header 731243ca327SMatthew Dillon */ 732243ca327SMatthew Dillon pickup.signature = 0; 733243ca327SMatthew Dillon pickup.type = 0; 734243ca327SMatthew Dillon 735243ca327SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 736243ca327SMatthew Dillon 737243ca327SMatthew Dillon /* 738243ca327SMatthew Dillon * Read and process bulk records 739243ca327SMatthew Dillon */ 740243ca327SMatthew Dillon for (;;) { 741243ca327SMatthew Dillon size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup); 742243ca327SMatthew Dillon if (size <= 0) 743243ca327SMatthew Dillon break; 74417dd83bcSMatthew Dillon offset = 0; 74517dd83bcSMatthew Dillon while (offset < size) { 74617dd83bcSMatthew Dillon mrec = (void *)((char *)buf + offset); 74717dd83bcSMatthew Dillon bytes = HAMMER_HEAD_DOALIGN(mrec->head.rec_size); 74817dd83bcSMatthew Dillon if (offset + bytes > size) { 74917dd83bcSMatthew Dillon fprintf(stderr, "Misaligned record\n"); 75017dd83bcSMatthew Dillon exit(1); 75117dd83bcSMatthew Dillon } 75217dd83bcSMatthew Dillon 75385a8e8a7SMatthew Dillon switch(mrec->head.type & HAMMER_MRECF_TYPE_MASK) { 75485a8e8a7SMatthew Dillon case HAMMER_MREC_TYPE_REC_BADCRC: 75517dd83bcSMatthew Dillon case HAMMER_MREC_TYPE_REC: 756243ca327SMatthew Dillon printf("Record obj=%016llx key=%016llx " 75785a8e8a7SMatthew Dillon "rt=%02x ot=%02x", 75817dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_id, 75917dd83bcSMatthew Dillon mrec->rec.leaf.base.key, 76017dd83bcSMatthew Dillon mrec->rec.leaf.base.rec_type, 76117dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_type); 76285a8e8a7SMatthew Dillon if (mrec->head.type == 76385a8e8a7SMatthew Dillon HAMMER_MREC_TYPE_REC_BADCRC) { 76485a8e8a7SMatthew Dillon printf(" (BAD CRC)"); 76585a8e8a7SMatthew Dillon } 76685a8e8a7SMatthew Dillon printf("\n"); 767243ca327SMatthew Dillon printf(" tids %016llx:%016llx data=%d\n", 76817dd83bcSMatthew Dillon mrec->rec.leaf.base.create_tid, 76917dd83bcSMatthew Dillon mrec->rec.leaf.base.delete_tid, 77017dd83bcSMatthew Dillon mrec->rec.leaf.data_len); 77117dd83bcSMatthew Dillon break; 77217dd83bcSMatthew Dillon case HAMMER_MREC_TYPE_PASS: 77317dd83bcSMatthew Dillon printf("Pass obj=%016llx key=%016llx " 77417dd83bcSMatthew Dillon "rt=%02x ot=%02x\n", 77517dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_id, 77617dd83bcSMatthew Dillon mrec->rec.leaf.base.key, 77717dd83bcSMatthew Dillon mrec->rec.leaf.base.rec_type, 77817dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_type); 77917dd83bcSMatthew Dillon printf(" tids %016llx:%016llx data=%d\n", 78017dd83bcSMatthew Dillon mrec->rec.leaf.base.create_tid, 78117dd83bcSMatthew Dillon mrec->rec.leaf.base.delete_tid, 78217dd83bcSMatthew Dillon mrec->rec.leaf.data_len); 78317dd83bcSMatthew Dillon break; 78417dd83bcSMatthew Dillon case HAMMER_MREC_TYPE_SKIP: 78517dd83bcSMatthew Dillon printf("Skip obj=%016llx key=%016llx rt=%02x to\n" 78617dd83bcSMatthew Dillon " obj=%016llx key=%016llx rt=%02x\n", 78717dd83bcSMatthew Dillon mrec->skip.skip_beg.obj_id, 78817dd83bcSMatthew Dillon mrec->skip.skip_beg.key, 78917dd83bcSMatthew Dillon mrec->skip.skip_beg.rec_type, 79017dd83bcSMatthew Dillon mrec->skip.skip_end.obj_id, 79117dd83bcSMatthew Dillon mrec->skip.skip_end.key, 79217dd83bcSMatthew Dillon mrec->skip.skip_end.rec_type); 79317dd83bcSMatthew Dillon default: 79417dd83bcSMatthew Dillon break; 79517dd83bcSMatthew Dillon } 79617dd83bcSMatthew Dillon offset += bytes; 797243ca327SMatthew Dillon } 798243ca327SMatthew Dillon } 799243ca327SMatthew Dillon 800243ca327SMatthew Dillon /* 801243ca327SMatthew Dillon * Read and process the termination sync record. 802243ca327SMatthew Dillon */ 803243ca327SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 80448eadef9SMatthew Dillon if (mrec == NULL || 80548eadef9SMatthew Dillon (mrec->head.type != HAMMER_MREC_TYPE_SYNC && 80648eadef9SMatthew Dillon mrec->head.type != HAMMER_MREC_TYPE_IDLE) 80748eadef9SMatthew Dillon ) { 808243ca327SMatthew Dillon fprintf(stderr, "Mirror-dump: Did not get termination " 809243ca327SMatthew Dillon "sync record\n"); 810243ca327SMatthew Dillon } 811a7fbbf91SMatthew Dillon } 812a7fbbf91SMatthew Dillon 813a7fbbf91SMatthew Dillon void 81448eadef9SMatthew Dillon hammer_cmd_mirror_copy(char **av, int ac, int streaming) 815a7fbbf91SMatthew Dillon { 81634ebae70SMatthew Dillon pid_t pid1; 81734ebae70SMatthew Dillon pid_t pid2; 81834ebae70SMatthew Dillon int fds[2]; 819243ca327SMatthew Dillon const char *xav[16]; 820243ca327SMatthew Dillon char tbuf[16]; 82134ebae70SMatthew Dillon char *ptr; 822243ca327SMatthew Dillon int xac; 82334ebae70SMatthew Dillon 82434ebae70SMatthew Dillon if (ac != 2) 82534ebae70SMatthew Dillon mirror_usage(1); 82634ebae70SMatthew Dillon 827e7f926a5SMatthew Dillon TwoWayPipeOpt = 1; 828*0bd7a37cSMatthew Dillon signal(SIGPIPE, SIG_IGN); 829e7f926a5SMatthew Dillon 830e7f926a5SMatthew Dillon again: 83134ebae70SMatthew Dillon if (pipe(fds) < 0) { 83234ebae70SMatthew Dillon perror("pipe"); 83334ebae70SMatthew Dillon exit(1); 83434ebae70SMatthew Dillon } 83534ebae70SMatthew Dillon 83634ebae70SMatthew Dillon /* 83734ebae70SMatthew Dillon * Source 83834ebae70SMatthew Dillon */ 83934ebae70SMatthew Dillon if ((pid1 = fork()) == 0) { 840*0bd7a37cSMatthew Dillon signal(SIGPIPE, SIG_DFL); 84134ebae70SMatthew Dillon dup2(fds[0], 0); 84234ebae70SMatthew Dillon dup2(fds[0], 1); 84334ebae70SMatthew Dillon close(fds[0]); 84434ebae70SMatthew Dillon close(fds[1]); 84534ebae70SMatthew Dillon if ((ptr = strchr(av[0], ':')) != NULL) { 84634ebae70SMatthew Dillon *ptr++ = 0; 847243ca327SMatthew Dillon xac = 0; 848243ca327SMatthew Dillon xav[xac++] = "ssh"; 849243ca327SMatthew Dillon xav[xac++] = av[0]; 850243ca327SMatthew Dillon xav[xac++] = "hammer"; 85148eadef9SMatthew Dillon 85248eadef9SMatthew Dillon switch(VerboseOpt) { 85348eadef9SMatthew Dillon case 0: 85448eadef9SMatthew Dillon break; 85548eadef9SMatthew Dillon case 1: 856243ca327SMatthew Dillon xav[xac++] = "-v"; 85748eadef9SMatthew Dillon break; 85848eadef9SMatthew Dillon case 2: 85948eadef9SMatthew Dillon xav[xac++] = "-vv"; 86048eadef9SMatthew Dillon break; 86148eadef9SMatthew Dillon default: 86248eadef9SMatthew Dillon xav[xac++] = "-vvv"; 86348eadef9SMatthew Dillon break; 86448eadef9SMatthew Dillon } 86507485271SMichael Neumann if (ForceYesOpt) { 86607485271SMichael Neumann xav[xac++] = "-y"; 86707485271SMichael Neumann } 868243ca327SMatthew Dillon xav[xac++] = "-2"; 869243ca327SMatthew Dillon if (TimeoutOpt) { 870243ca327SMatthew Dillon snprintf(tbuf, sizeof(tbuf), "%d", TimeoutOpt); 871243ca327SMatthew Dillon xav[xac++] = "-t"; 872243ca327SMatthew Dillon xav[xac++] = tbuf; 873243ca327SMatthew Dillon } 87448eadef9SMatthew Dillon if (streaming) 875901f434aSMatthew Dillon xav[xac++] = "mirror-read-stream"; 87648eadef9SMatthew Dillon else 877243ca327SMatthew Dillon xav[xac++] = "mirror-read"; 878243ca327SMatthew Dillon xav[xac++] = ptr; 879243ca327SMatthew Dillon xav[xac++] = NULL; 880243ca327SMatthew Dillon execv("/usr/bin/ssh", (void *)xav); 88134ebae70SMatthew Dillon } else { 88248eadef9SMatthew Dillon hammer_cmd_mirror_read(av, 1, streaming); 883243ca327SMatthew Dillon fflush(stdout); 884243ca327SMatthew Dillon fflush(stderr); 88534ebae70SMatthew Dillon } 88653d93cc7SMatthew Dillon _exit(1); 88734ebae70SMatthew Dillon } 88834ebae70SMatthew Dillon 88934ebae70SMatthew Dillon /* 89034ebae70SMatthew Dillon * Target 89134ebae70SMatthew Dillon */ 89234ebae70SMatthew Dillon if ((pid2 = fork()) == 0) { 893*0bd7a37cSMatthew Dillon signal(SIGPIPE, SIG_DFL); 89434ebae70SMatthew Dillon dup2(fds[1], 0); 89534ebae70SMatthew Dillon dup2(fds[1], 1); 89634ebae70SMatthew Dillon close(fds[0]); 89734ebae70SMatthew Dillon close(fds[1]); 89834ebae70SMatthew Dillon if ((ptr = strchr(av[1], ':')) != NULL) { 89934ebae70SMatthew Dillon *ptr++ = 0; 900243ca327SMatthew Dillon xac = 0; 901243ca327SMatthew Dillon xav[xac++] = "ssh"; 902243ca327SMatthew Dillon xav[xac++] = av[1]; 903243ca327SMatthew Dillon xav[xac++] = "hammer"; 90448eadef9SMatthew Dillon 90548eadef9SMatthew Dillon switch(VerboseOpt) { 90648eadef9SMatthew Dillon case 0: 90748eadef9SMatthew Dillon break; 90848eadef9SMatthew Dillon case 1: 909243ca327SMatthew Dillon xav[xac++] = "-v"; 91048eadef9SMatthew Dillon break; 91148eadef9SMatthew Dillon case 2: 91248eadef9SMatthew Dillon xav[xac++] = "-vv"; 91348eadef9SMatthew Dillon break; 91448eadef9SMatthew Dillon default: 91548eadef9SMatthew Dillon xav[xac++] = "-vvv"; 91648eadef9SMatthew Dillon break; 91748eadef9SMatthew Dillon } 91807485271SMichael Neumann if (ForceYesOpt) { 91907485271SMichael Neumann xav[xac++] = "-y"; 92007485271SMichael Neumann } 921243ca327SMatthew Dillon xav[xac++] = "-2"; 922243ca327SMatthew Dillon xav[xac++] = "mirror-write"; 923243ca327SMatthew Dillon xav[xac++] = ptr; 924243ca327SMatthew Dillon xav[xac++] = NULL; 925243ca327SMatthew Dillon execv("/usr/bin/ssh", (void *)xav); 92634ebae70SMatthew Dillon } else { 92734ebae70SMatthew Dillon hammer_cmd_mirror_write(av + 1, 1); 928243ca327SMatthew Dillon fflush(stdout); 929243ca327SMatthew Dillon fflush(stderr); 93034ebae70SMatthew Dillon } 93153d93cc7SMatthew Dillon _exit(1); 93234ebae70SMatthew Dillon } 93334ebae70SMatthew Dillon close(fds[0]); 93434ebae70SMatthew Dillon close(fds[1]); 93534ebae70SMatthew Dillon 93634ebae70SMatthew Dillon while (waitpid(pid1, NULL, 0) <= 0) 93734ebae70SMatthew Dillon ; 93834ebae70SMatthew Dillon while (waitpid(pid2, NULL, 0) <= 0) 93934ebae70SMatthew Dillon ; 940e7f926a5SMatthew Dillon 941e7f926a5SMatthew Dillon /* 942e7f926a5SMatthew Dillon * If the link is lost restart 943e7f926a5SMatthew Dillon */ 944e7f926a5SMatthew Dillon if (streaming) { 945e7f926a5SMatthew Dillon if (VerboseOpt) { 946e7f926a5SMatthew Dillon fprintf(stderr, "\nLost Link\n"); 947e7f926a5SMatthew Dillon fflush(stderr); 948e7f926a5SMatthew Dillon } 949*0bd7a37cSMatthew Dillon sleep(15 + DelayOpt); 950e7f926a5SMatthew Dillon goto again; 951e7f926a5SMatthew Dillon } 952e7f926a5SMatthew Dillon 953a7fbbf91SMatthew Dillon } 954a7fbbf91SMatthew Dillon 955243ca327SMatthew Dillon /* 956243ca327SMatthew Dillon * Read and return multiple mrecords 957243ca327SMatthew Dillon */ 958a7fbbf91SMatthew Dillon static int 95917dd83bcSMatthew Dillon read_mrecords(int fd, char *buf, u_int size, hammer_ioc_mrecord_head_t pickup) 960a7fbbf91SMatthew Dillon { 96117dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 962a7fbbf91SMatthew Dillon u_int count; 963a7fbbf91SMatthew Dillon size_t n; 964a7fbbf91SMatthew Dillon size_t i; 96517dd83bcSMatthew Dillon size_t bytes; 96685a8e8a7SMatthew Dillon int type; 967a7fbbf91SMatthew Dillon 968a7fbbf91SMatthew Dillon count = 0; 969a7fbbf91SMatthew Dillon while (size - count >= HAMMER_MREC_HEADSIZE) { 970a7fbbf91SMatthew Dillon /* 971a7fbbf91SMatthew Dillon * Cached the record header in case we run out of buffer 972a7fbbf91SMatthew Dillon * space. 973a7fbbf91SMatthew Dillon */ 97417dd83bcSMatthew Dillon fflush(stdout); 975a7fbbf91SMatthew Dillon if (pickup->signature == 0) { 976a7fbbf91SMatthew Dillon for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) { 977a7fbbf91SMatthew Dillon i = read(fd, (char *)pickup + n, 978a7fbbf91SMatthew Dillon HAMMER_MREC_HEADSIZE - n); 979a7fbbf91SMatthew Dillon if (i <= 0) 980a7fbbf91SMatthew Dillon break; 981a7fbbf91SMatthew Dillon } 982a7fbbf91SMatthew Dillon if (n == 0) 983a7fbbf91SMatthew Dillon break; 984a7fbbf91SMatthew Dillon if (n != HAMMER_MREC_HEADSIZE) { 985a7fbbf91SMatthew Dillon fprintf(stderr, "read_mrecords: short read on pipe\n"); 986a7fbbf91SMatthew Dillon exit(1); 987a7fbbf91SMatthew Dillon } 988a7fbbf91SMatthew Dillon if (pickup->signature != HAMMER_IOC_MIRROR_SIGNATURE) { 98934bb69d8SThomas Nikolajsen fprintf(stderr, "read_mrecords: malformed record on pipe, " 99034bb69d8SThomas Nikolajsen "bad signature\n"); 991a7fbbf91SMatthew Dillon exit(1); 992a7fbbf91SMatthew Dillon } 993a7fbbf91SMatthew Dillon } 994a7fbbf91SMatthew Dillon if (pickup->rec_size < HAMMER_MREC_HEADSIZE || 99517dd83bcSMatthew Dillon pickup->rec_size > sizeof(*mrec) + HAMMER_XBUFSIZE) { 99634bb69d8SThomas Nikolajsen fprintf(stderr, "read_mrecords: malformed record on pipe, " 99734bb69d8SThomas Nikolajsen "illegal rec_size\n"); 998a7fbbf91SMatthew Dillon exit(1); 999a7fbbf91SMatthew Dillon } 1000a7fbbf91SMatthew Dillon 1001a7fbbf91SMatthew Dillon /* 1002a7fbbf91SMatthew Dillon * Stop if we have insufficient space for the record and data. 1003a7fbbf91SMatthew Dillon */ 100417dd83bcSMatthew Dillon bytes = HAMMER_HEAD_DOALIGN(pickup->rec_size); 100517dd83bcSMatthew Dillon if (size - count < bytes) 1006a7fbbf91SMatthew Dillon break; 1007a7fbbf91SMatthew Dillon 1008a7fbbf91SMatthew Dillon /* 100985a8e8a7SMatthew Dillon * Stop if the record type is not a REC, SKIP, or PASS, 101085a8e8a7SMatthew Dillon * which are the only types the ioctl supports. Other types 101185a8e8a7SMatthew Dillon * are used only by the userland protocol. 101285a8e8a7SMatthew Dillon * 101385a8e8a7SMatthew Dillon * Ignore all flags. 1014243ca327SMatthew Dillon */ 101585a8e8a7SMatthew Dillon type = pickup->type & HAMMER_MRECF_TYPE_LOMASK; 101685a8e8a7SMatthew Dillon if (type != HAMMER_MREC_TYPE_PFSD && 101785a8e8a7SMatthew Dillon type != HAMMER_MREC_TYPE_REC && 101885a8e8a7SMatthew Dillon type != HAMMER_MREC_TYPE_SKIP && 101985a8e8a7SMatthew Dillon type != HAMMER_MREC_TYPE_PASS) { 1020243ca327SMatthew Dillon break; 102117dd83bcSMatthew Dillon } 1022243ca327SMatthew Dillon 1023243ca327SMatthew Dillon /* 1024a7fbbf91SMatthew Dillon * Read the remainder and clear the pickup signature. 1025a7fbbf91SMatthew Dillon */ 102617dd83bcSMatthew Dillon for (n = HAMMER_MREC_HEADSIZE; n < bytes; n += i) { 102717dd83bcSMatthew Dillon i = read(fd, buf + count + n, bytes - n); 1028a7fbbf91SMatthew Dillon if (i <= 0) 1029a7fbbf91SMatthew Dillon break; 1030a7fbbf91SMatthew Dillon } 103117dd83bcSMatthew Dillon if (n != bytes) { 1032a7fbbf91SMatthew Dillon fprintf(stderr, "read_mrecords: short read on pipe\n"); 1033a7fbbf91SMatthew Dillon exit(1); 1034a7fbbf91SMatthew Dillon } 103517dd83bcSMatthew Dillon 103617dd83bcSMatthew Dillon bcopy(pickup, buf + count, HAMMER_MREC_HEADSIZE); 103717dd83bcSMatthew Dillon pickup->signature = 0; 103817dd83bcSMatthew Dillon pickup->type = 0; 103917dd83bcSMatthew Dillon mrec = (void *)(buf + count); 104017dd83bcSMatthew Dillon 104117dd83bcSMatthew Dillon /* 104217dd83bcSMatthew Dillon * Validate the completed record 104317dd83bcSMatthew Dillon */ 104417dd83bcSMatthew Dillon if (mrec->head.rec_crc != 104517dd83bcSMatthew Dillon crc32((char *)mrec + HAMMER_MREC_CRCOFF, 104617dd83bcSMatthew Dillon mrec->head.rec_size - HAMMER_MREC_CRCOFF)) { 104717dd83bcSMatthew Dillon fprintf(stderr, "read_mrecords: malformed record " 104817dd83bcSMatthew Dillon "on pipe, bad crc\n"); 104917dd83bcSMatthew Dillon exit(1); 1050a7fbbf91SMatthew Dillon } 1051a7fbbf91SMatthew Dillon 105217dd83bcSMatthew Dillon /* 105385a8e8a7SMatthew Dillon * If its a B-Tree record validate the data crc. 105485a8e8a7SMatthew Dillon * 105585a8e8a7SMatthew Dillon * NOTE: If the VFS passes us an explicitly errorde mrec 105685a8e8a7SMatthew Dillon * we just pass it through. 105717dd83bcSMatthew Dillon */ 105885a8e8a7SMatthew Dillon type = mrec->head.type & HAMMER_MRECF_TYPE_MASK; 105985a8e8a7SMatthew Dillon 106085a8e8a7SMatthew Dillon if (type == HAMMER_MREC_TYPE_REC) { 106117dd83bcSMatthew Dillon if (mrec->head.rec_size < 106217dd83bcSMatthew Dillon sizeof(mrec->rec) + mrec->rec.leaf.data_len) { 106317dd83bcSMatthew Dillon fprintf(stderr, 106417dd83bcSMatthew Dillon "read_mrecords: malformed record on " 106517dd83bcSMatthew Dillon "pipe, illegal element data_len\n"); 106617dd83bcSMatthew Dillon exit(1); 106717dd83bcSMatthew Dillon } 106817dd83bcSMatthew Dillon if (mrec->rec.leaf.data_len && 106917dd83bcSMatthew Dillon mrec->rec.leaf.data_offset && 107017dd83bcSMatthew Dillon hammer_crc_test_leaf(&mrec->rec + 1, &mrec->rec.leaf) == 0) { 107117dd83bcSMatthew Dillon fprintf(stderr, 107217dd83bcSMatthew Dillon "read_mrecords: data_crc did not " 107317dd83bcSMatthew Dillon "match data! obj=%016llx key=%016llx\n", 107417dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_id, 107517dd83bcSMatthew Dillon mrec->rec.leaf.base.key); 107617dd83bcSMatthew Dillon fprintf(stderr, 107717dd83bcSMatthew Dillon "continuing, but there are problems\n"); 107817dd83bcSMatthew Dillon } 107917dd83bcSMatthew Dillon } 108017dd83bcSMatthew Dillon count += bytes; 1081a7fbbf91SMatthew Dillon } 1082a7fbbf91SMatthew Dillon return(count); 1083a7fbbf91SMatthew Dillon } 1084a7fbbf91SMatthew Dillon 108534ebae70SMatthew Dillon /* 108617dd83bcSMatthew Dillon * Read and return a single mrecord. 1087243ca327SMatthew Dillon */ 1088243ca327SMatthew Dillon static 108917dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t 109017dd83bcSMatthew Dillon read_mrecord(int fdin, int *errorp, hammer_ioc_mrecord_head_t pickup) 1091243ca327SMatthew Dillon { 109217dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 109317dd83bcSMatthew Dillon struct hammer_ioc_mrecord_head mrechd; 1094243ca327SMatthew Dillon size_t bytes; 1095243ca327SMatthew Dillon size_t n; 1096243ca327SMatthew Dillon size_t i; 1097243ca327SMatthew Dillon 1098243ca327SMatthew Dillon if (pickup && pickup->type != 0) { 1099243ca327SMatthew Dillon mrechd = *pickup; 1100243ca327SMatthew Dillon pickup->signature = 0; 1101243ca327SMatthew Dillon pickup->type = 0; 1102243ca327SMatthew Dillon n = HAMMER_MREC_HEADSIZE; 1103243ca327SMatthew Dillon } else { 1104243ca327SMatthew Dillon /* 1105243ca327SMatthew Dillon * Read in the PFSD header from the sender. 1106243ca327SMatthew Dillon */ 1107243ca327SMatthew Dillon for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) { 1108243ca327SMatthew Dillon i = read(fdin, (char *)&mrechd + n, HAMMER_MREC_HEADSIZE - n); 1109243ca327SMatthew Dillon if (i <= 0) 1110243ca327SMatthew Dillon break; 1111243ca327SMatthew Dillon } 1112243ca327SMatthew Dillon if (n == 0) { 1113243ca327SMatthew Dillon *errorp = 0; /* EOF */ 1114243ca327SMatthew Dillon return(NULL); 1115243ca327SMatthew Dillon } 1116243ca327SMatthew Dillon if (n != HAMMER_MREC_HEADSIZE) { 1117243ca327SMatthew Dillon fprintf(stderr, "short read of mrecord header\n"); 1118243ca327SMatthew Dillon *errorp = EPIPE; 1119243ca327SMatthew Dillon return(NULL); 1120243ca327SMatthew Dillon } 1121243ca327SMatthew Dillon } 1122243ca327SMatthew Dillon if (mrechd.signature != HAMMER_IOC_MIRROR_SIGNATURE) { 1123243ca327SMatthew Dillon fprintf(stderr, "read_mrecord: bad signature\n"); 1124243ca327SMatthew Dillon *errorp = EINVAL; 1125243ca327SMatthew Dillon return(NULL); 1126243ca327SMatthew Dillon } 112717dd83bcSMatthew Dillon bytes = HAMMER_HEAD_DOALIGN(mrechd.rec_size); 112817dd83bcSMatthew Dillon assert(bytes >= sizeof(mrechd)); 1129243ca327SMatthew Dillon mrec = malloc(bytes); 113017dd83bcSMatthew Dillon mrec->head = mrechd; 113117dd83bcSMatthew Dillon 1132243ca327SMatthew Dillon while (n < bytes) { 1133243ca327SMatthew Dillon i = read(fdin, (char *)mrec + n, bytes - n); 1134243ca327SMatthew Dillon if (i <= 0) 1135243ca327SMatthew Dillon break; 1136243ca327SMatthew Dillon n += i; 1137243ca327SMatthew Dillon } 1138243ca327SMatthew Dillon if (n != bytes) { 1139243ca327SMatthew Dillon fprintf(stderr, "read_mrecord: short read on payload\n"); 1140243ca327SMatthew Dillon *errorp = EPIPE; 1141243ca327SMatthew Dillon return(NULL); 1142243ca327SMatthew Dillon } 114317dd83bcSMatthew Dillon if (mrec->head.rec_crc != 114417dd83bcSMatthew Dillon crc32((char *)mrec + HAMMER_MREC_CRCOFF, 114517dd83bcSMatthew Dillon mrec->head.rec_size - HAMMER_MREC_CRCOFF)) { 1146243ca327SMatthew Dillon fprintf(stderr, "read_mrecord: bad CRC\n"); 1147243ca327SMatthew Dillon *errorp = EINVAL; 1148243ca327SMatthew Dillon return(NULL); 1149243ca327SMatthew Dillon } 1150243ca327SMatthew Dillon *errorp = 0; 1151243ca327SMatthew Dillon return(mrec); 1152243ca327SMatthew Dillon } 1153243ca327SMatthew Dillon 1154243ca327SMatthew Dillon static 1155243ca327SMatthew Dillon void 115617dd83bcSMatthew Dillon write_mrecord(int fdout, u_int32_t type, hammer_ioc_mrecord_any_t mrec, 115717dd83bcSMatthew Dillon int bytes) 1158243ca327SMatthew Dillon { 115917dd83bcSMatthew Dillon char zbuf[HAMMER_HEAD_ALIGN]; 116017dd83bcSMatthew Dillon int pad; 1161243ca327SMatthew Dillon 116217dd83bcSMatthew Dillon pad = HAMMER_HEAD_DOALIGN(bytes) - bytes; 116317dd83bcSMatthew Dillon 116417dd83bcSMatthew Dillon assert(bytes >= (int)sizeof(mrec->head)); 116517dd83bcSMatthew Dillon bzero(&mrec->head, sizeof(mrec->head)); 116617dd83bcSMatthew Dillon mrec->head.signature = HAMMER_IOC_MIRROR_SIGNATURE; 116717dd83bcSMatthew Dillon mrec->head.type = type; 116817dd83bcSMatthew Dillon mrec->head.rec_size = bytes; 116917dd83bcSMatthew Dillon mrec->head.rec_crc = crc32((char *)mrec + HAMMER_MREC_CRCOFF, 117017dd83bcSMatthew Dillon bytes - HAMMER_MREC_CRCOFF); 117117dd83bcSMatthew Dillon if (write(fdout, mrec, bytes) != bytes) { 1172243ca327SMatthew Dillon fprintf(stderr, "write_mrecord: error %d (%s)\n", 1173243ca327SMatthew Dillon errno, strerror(errno)); 1174243ca327SMatthew Dillon exit(1); 1175243ca327SMatthew Dillon } 117617dd83bcSMatthew Dillon if (pad) { 117717dd83bcSMatthew Dillon bzero(zbuf, pad); 117817dd83bcSMatthew Dillon if (write(fdout, zbuf, pad) != pad) { 117917dd83bcSMatthew Dillon fprintf(stderr, "write_mrecord: error %d (%s)\n", 118017dd83bcSMatthew Dillon errno, strerror(errno)); 118117dd83bcSMatthew Dillon exit(1); 118217dd83bcSMatthew Dillon } 118317dd83bcSMatthew Dillon } 1184243ca327SMatthew Dillon } 1185243ca327SMatthew Dillon 1186243ca327SMatthew Dillon /* 118734ebae70SMatthew Dillon * Generate a mirroring header with the pfs information of the 118834ebae70SMatthew Dillon * originating filesytem. 118934ebae70SMatthew Dillon */ 119034ebae70SMatthew Dillon static void 1191e7f926a5SMatthew Dillon generate_mrec_header(int fd, int pfs_id, 1192e7f926a5SMatthew Dillon union hammer_ioc_mrecord_any *mrec_tmp) 119334ebae70SMatthew Dillon { 119434ebae70SMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 119534ebae70SMatthew Dillon 119634ebae70SMatthew Dillon bzero(&pfs, sizeof(pfs)); 1197e7f926a5SMatthew Dillon bzero(mrec_tmp, sizeof(*mrec_tmp)); 1198d4e5b69bSMatthew Dillon pfs.pfs_id = pfs_id; 1199e7f926a5SMatthew Dillon pfs.ondisk = &mrec_tmp->pfs.pfsd; 1200e7f926a5SMatthew Dillon pfs.bytes = sizeof(mrec_tmp->pfs.pfsd); 120134ebae70SMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 12029dc76cb1SMatthew Dillon fprintf(stderr, "Mirror-read: not a HAMMER fs/pseudofs!\n"); 120334ebae70SMatthew Dillon exit(1); 120434ebae70SMatthew Dillon } 120534ebae70SMatthew Dillon if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) { 12069dc76cb1SMatthew Dillon fprintf(stderr, "Mirror-read: HAMMER pfs version mismatch!\n"); 120734ebae70SMatthew Dillon exit(1); 120834ebae70SMatthew Dillon } 1209e7f926a5SMatthew Dillon mrec_tmp->pfs.version = pfs.version; 121034ebae70SMatthew Dillon } 121134ebae70SMatthew Dillon 121234ebae70SMatthew Dillon /* 121334ebae70SMatthew Dillon * Validate the pfs information from the originating filesystem 121434ebae70SMatthew Dillon * against the target filesystem. shared_uuid must match. 121548eadef9SMatthew Dillon * 121648eadef9SMatthew Dillon * return -1 if we got a TERM record 121734ebae70SMatthew Dillon */ 121848eadef9SMatthew Dillon static int 1219d4e5b69bSMatthew Dillon validate_mrec_header(int fd, int fdin, int is_target, int pfs_id, 122048eadef9SMatthew Dillon struct hammer_ioc_mrecord_head *pickup, 122134ebae70SMatthew Dillon hammer_tid_t *tid_begp, hammer_tid_t *tid_endp) 122234ebae70SMatthew Dillon { 122334ebae70SMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 122434ebae70SMatthew Dillon struct hammer_pseudofs_data pfsd; 122517dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 1226243ca327SMatthew Dillon int error; 122734ebae70SMatthew Dillon 122834ebae70SMatthew Dillon /* 122934ebae70SMatthew Dillon * Get the PFSD info from the target filesystem. 123034ebae70SMatthew Dillon */ 123134ebae70SMatthew Dillon bzero(&pfs, sizeof(pfs)); 123234ebae70SMatthew Dillon bzero(&pfsd, sizeof(pfsd)); 1233d4e5b69bSMatthew Dillon pfs.pfs_id = pfs_id; 123434ebae70SMatthew Dillon pfs.ondisk = &pfsd; 123534ebae70SMatthew Dillon pfs.bytes = sizeof(pfsd); 123634ebae70SMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 123734ebae70SMatthew Dillon fprintf(stderr, "mirror-write: not a HAMMER fs/pseudofs!\n"); 123834ebae70SMatthew Dillon exit(1); 123934ebae70SMatthew Dillon } 124034ebae70SMatthew Dillon if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) { 124134ebae70SMatthew Dillon fprintf(stderr, "mirror-write: HAMMER pfs version mismatch!\n"); 124234ebae70SMatthew Dillon exit(1); 124334ebae70SMatthew Dillon } 124434ebae70SMatthew Dillon 124548eadef9SMatthew Dillon mrec = read_mrecord(fdin, &error, pickup); 1246243ca327SMatthew Dillon if (mrec == NULL) { 1247243ca327SMatthew Dillon if (error == 0) 1248243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: short read\n"); 124934ebae70SMatthew Dillon exit(1); 125034ebae70SMatthew Dillon } 125148eadef9SMatthew Dillon if (mrec->head.type == HAMMER_MREC_TYPE_TERM) { 125248eadef9SMatthew Dillon free(mrec); 125348eadef9SMatthew Dillon return(-1); 125448eadef9SMatthew Dillon } 125548eadef9SMatthew Dillon 125617dd83bcSMatthew Dillon if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) { 1257243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: did not get expected " 1258243ca327SMatthew Dillon "PFSD record type\n"); 125934ebae70SMatthew Dillon exit(1); 126034ebae70SMatthew Dillon } 126117dd83bcSMatthew Dillon if (mrec->head.rec_size != sizeof(mrec->pfs)) { 1262243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: unexpected payload " 1263243ca327SMatthew Dillon "size\n"); 126434ebae70SMatthew Dillon exit(1); 126534ebae70SMatthew Dillon } 126617dd83bcSMatthew Dillon if (mrec->pfs.version != pfs.version) { 1267243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: Version mismatch\n"); 126834ebae70SMatthew Dillon exit(1); 126934ebae70SMatthew Dillon } 127034ebae70SMatthew Dillon 127134ebae70SMatthew Dillon /* 127234ebae70SMatthew Dillon * Whew. Ok, is the read PFS info compatible with the target? 127334ebae70SMatthew Dillon */ 127417dd83bcSMatthew Dillon if (bcmp(&mrec->pfs.pfsd.shared_uuid, &pfsd.shared_uuid, 127517dd83bcSMatthew Dillon sizeof(pfsd.shared_uuid)) != 0) { 127617dd83bcSMatthew Dillon fprintf(stderr, 127717dd83bcSMatthew Dillon "mirror-write: source and target have " 1278f265b84fSSascha Wildner "different shared-uuid's!\n"); 127934ebae70SMatthew Dillon exit(1); 128034ebae70SMatthew Dillon } 1281d4e5b69bSMatthew Dillon if (is_target && 1282d4e5b69bSMatthew Dillon (pfsd.mirror_flags & HAMMER_PFSD_SLAVE) == 0) { 128334ebae70SMatthew Dillon fprintf(stderr, "mirror-write: target must be in slave mode\n"); 128434ebae70SMatthew Dillon exit(1); 128534ebae70SMatthew Dillon } 1286d4e5b69bSMatthew Dillon if (tid_begp) 128717dd83bcSMatthew Dillon *tid_begp = mrec->pfs.pfsd.sync_beg_tid; 1288d4e5b69bSMatthew Dillon if (tid_endp) 128917dd83bcSMatthew Dillon *tid_endp = mrec->pfs.pfsd.sync_end_tid; 1290243ca327SMatthew Dillon free(mrec); 129148eadef9SMatthew Dillon return(0); 129234ebae70SMatthew Dillon } 129334ebae70SMatthew Dillon 129434ebae70SMatthew Dillon static void 1295d4e5b69bSMatthew Dillon update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id) 129634ebae70SMatthew Dillon { 1297243ca327SMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 1298243ca327SMatthew Dillon struct hammer_pseudofs_data pfsd; 129934ebae70SMatthew Dillon 1300243ca327SMatthew Dillon bzero(&pfs, sizeof(pfs)); 1301243ca327SMatthew Dillon bzero(&pfsd, sizeof(pfsd)); 1302d4e5b69bSMatthew Dillon pfs.pfs_id = pfs_id; 1303243ca327SMatthew Dillon pfs.ondisk = &pfsd; 1304243ca327SMatthew Dillon pfs.bytes = sizeof(pfsd); 1305243ca327SMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 1306243ca327SMatthew Dillon perror("update_pfs_snapshot (read)"); 1307243ca327SMatthew Dillon exit(1); 130834ebae70SMatthew Dillon } 13099c67b4d2SMatthew Dillon if (pfsd.sync_end_tid != snapshot_tid) { 1310ddc8e722SMatthew Dillon pfsd.sync_end_tid = snapshot_tid; 1311243ca327SMatthew Dillon if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) != 0) { 1312243ca327SMatthew Dillon perror("update_pfs_snapshot (rewrite)"); 1313243ca327SMatthew Dillon exit(1); 131434ebae70SMatthew Dillon } 131548eadef9SMatthew Dillon if (VerboseOpt >= 2) { 13169dc76cb1SMatthew Dillon fprintf(stderr, 13179dc76cb1SMatthew Dillon "Mirror-write: Completed, updated snapshot " 13189dc76cb1SMatthew Dillon "to %016llx\n", 13199dc76cb1SMatthew Dillon snapshot_tid); 1320243ca327SMatthew Dillon } 13219c67b4d2SMatthew Dillon } 132248eadef9SMatthew Dillon } 1323243ca327SMatthew Dillon 132448eadef9SMatthew Dillon /* 132548eadef9SMatthew Dillon * Bandwidth-limited write in chunks 132648eadef9SMatthew Dillon */ 132748eadef9SMatthew Dillon static 132848eadef9SMatthew Dillon ssize_t 132948eadef9SMatthew Dillon writebw(int fd, const void *buf, size_t nbytes, 133048eadef9SMatthew Dillon u_int64_t *bwcount, struct timeval *tv1) 133148eadef9SMatthew Dillon { 133248eadef9SMatthew Dillon struct timeval tv2; 133348eadef9SMatthew Dillon size_t n; 133448eadef9SMatthew Dillon ssize_t r; 133548eadef9SMatthew Dillon ssize_t a; 133648eadef9SMatthew Dillon int usec; 133748eadef9SMatthew Dillon 133848eadef9SMatthew Dillon a = 0; 133948eadef9SMatthew Dillon r = 0; 134048eadef9SMatthew Dillon while (nbytes) { 134148eadef9SMatthew Dillon if (*bwcount + nbytes > BandwidthOpt) 134248eadef9SMatthew Dillon n = BandwidthOpt - *bwcount; 134348eadef9SMatthew Dillon else 134448eadef9SMatthew Dillon n = nbytes; 134548eadef9SMatthew Dillon if (n) 134648eadef9SMatthew Dillon r = write(fd, buf, n); 134748eadef9SMatthew Dillon if (r >= 0) { 134848eadef9SMatthew Dillon a += r; 134948eadef9SMatthew Dillon nbytes -= r; 135048eadef9SMatthew Dillon buf = (const char *)buf + r; 135148eadef9SMatthew Dillon } 135248eadef9SMatthew Dillon if ((size_t)r != n) 135348eadef9SMatthew Dillon break; 135448eadef9SMatthew Dillon *bwcount += n; 135548eadef9SMatthew Dillon if (*bwcount >= BandwidthOpt) { 135648eadef9SMatthew Dillon gettimeofday(&tv2, NULL); 135748eadef9SMatthew Dillon usec = (int)(tv2.tv_sec - tv1->tv_sec) * 1000000 + 135848eadef9SMatthew Dillon (int)(tv2.tv_usec - tv1->tv_usec); 135948eadef9SMatthew Dillon if (usec >= 0 && usec < 1000000) 136048eadef9SMatthew Dillon usleep(1000000 - usec); 136148eadef9SMatthew Dillon gettimeofday(tv1, NULL); 136248eadef9SMatthew Dillon *bwcount -= BandwidthOpt; 136348eadef9SMatthew Dillon } 136448eadef9SMatthew Dillon } 136548eadef9SMatthew Dillon return(a ? a : r); 136648eadef9SMatthew Dillon } 136734ebae70SMatthew Dillon 136801a72c9fSMatthew Dillon /* 136901a72c9fSMatthew Dillon * Get a yes or no answer from the terminal. The program may be run as 137001a72c9fSMatthew Dillon * part of a two-way pipe so we cannot use stdin for this operation. 137101a72c9fSMatthew Dillon */ 137201a72c9fSMatthew Dillon static int 137301a72c9fSMatthew Dillon getyn(void) 137401a72c9fSMatthew Dillon { 137501a72c9fSMatthew Dillon char buf[256]; 137601a72c9fSMatthew Dillon FILE *fp; 137701a72c9fSMatthew Dillon int result; 137801a72c9fSMatthew Dillon 137901a72c9fSMatthew Dillon fp = fopen("/dev/tty", "r"); 138001a72c9fSMatthew Dillon if (fp == NULL) { 138101a72c9fSMatthew Dillon fprintf(stderr, "No terminal for response\n"); 138201a72c9fSMatthew Dillon return(-1); 138301a72c9fSMatthew Dillon } 138401a72c9fSMatthew Dillon result = -1; 138501a72c9fSMatthew Dillon while (fgets(buf, sizeof(buf), fp) != NULL) { 138601a72c9fSMatthew Dillon if (buf[0] == 'y' || buf[0] == 'Y') { 138701a72c9fSMatthew Dillon result = 1; 138801a72c9fSMatthew Dillon break; 138901a72c9fSMatthew Dillon } 139001a72c9fSMatthew Dillon if (buf[0] == 'n' || buf[0] == 'N') { 139101a72c9fSMatthew Dillon result = 0; 139201a72c9fSMatthew Dillon break; 139301a72c9fSMatthew Dillon } 139401a72c9fSMatthew Dillon fprintf(stderr, "Response not understood\n"); 139501a72c9fSMatthew Dillon break; 139601a72c9fSMatthew Dillon } 139701a72c9fSMatthew Dillon fclose(fp); 139801a72c9fSMatthew Dillon return(result); 139901a72c9fSMatthew Dillon } 140001a72c9fSMatthew Dillon 1401a7fbbf91SMatthew Dillon static void 1402a7fbbf91SMatthew Dillon mirror_usage(int code) 1403a7fbbf91SMatthew Dillon { 1404a7fbbf91SMatthew Dillon fprintf(stderr, 140534bb69d8SThomas Nikolajsen "hammer mirror-read <filesystem> [begin-tid]\n" 140634bb69d8SThomas Nikolajsen "hammer mirror-read-stream <filesystem> [begin-tid]\n" 1407a7fbbf91SMatthew Dillon "hammer mirror-write <filesystem>\n" 1408243ca327SMatthew Dillon "hammer mirror-dump\n" 140934bb69d8SThomas Nikolajsen "hammer mirror-copy [[user@]host:]<filesystem>" 141034bb69d8SThomas Nikolajsen " [[user@]host:]<filesystem>\n" 141134bb69d8SThomas Nikolajsen "hammer mirror-stream [[user@]host:]<filesystem>" 141234bb69d8SThomas Nikolajsen " [[user@]host:]<filesystem>\n" 1413a7fbbf91SMatthew Dillon ); 1414a7fbbf91SMatthew Dillon exit(code); 1415a7fbbf91SMatthew Dillon } 141601a72c9fSMatthew Dillon 1417