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 * 34*17dd83bcSMatthew Dillon * $DragonFly: src/sbin/hammer/cmd_mirror.c,v 1.7 2008/07/10 04:44:58 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, 42*17dd83bcSMatthew Dillon hammer_ioc_mrecord_head_t pickup); 43*17dd83bcSMatthew Dillon static hammer_ioc_mrecord_any_t read_mrecord(int fdin, int *errorp, 44*17dd83bcSMatthew Dillon hammer_ioc_mrecord_head_t pickup); 45*17dd83bcSMatthew Dillon static void write_mrecord(int fdout, u_int32_t type, 46*17dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec, int bytes); 47d4e5b69bSMatthew Dillon static void generate_mrec_header(int fd, int fdout, int pfs_id, 4834ebae70SMatthew Dillon hammer_tid_t *tid_begp, hammer_tid_t *tid_endp); 49d4e5b69bSMatthew Dillon static void validate_mrec_header(int fd, int fdin, int is_target, int pfs_id, 5034ebae70SMatthew Dillon hammer_tid_t *tid_begp, hammer_tid_t *tid_endp); 51d4e5b69bSMatthew Dillon static void update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id); 52a7fbbf91SMatthew Dillon static void mirror_usage(int code); 53a7fbbf91SMatthew Dillon 54*17dd83bcSMatthew Dillon /* 55*17dd83bcSMatthew Dillon * Generate a mirroring data stream from the specific source over the 56*17dd83bcSMatthew Dillon * entire key range, but restricted to the specified transaction range. 57*17dd83bcSMatthew Dillon * 58*17dd83bcSMatthew Dillon * The HAMMER VFS does most of the work, we add a few new mrecord 59*17dd83bcSMatthew Dillon * types to negotiate the TID ranges and verify that the entire 60*17dd83bcSMatthew Dillon * stream made it to the destination. 61*17dd83bcSMatthew Dillon */ 62a7fbbf91SMatthew Dillon void 63a7fbbf91SMatthew Dillon hammer_cmd_mirror_read(char **av, int ac) 64a7fbbf91SMatthew Dillon { 65a7fbbf91SMatthew Dillon struct hammer_ioc_mirror_rw mirror; 66d4e5b69bSMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 67*17dd83bcSMatthew Dillon union hammer_ioc_mrecord_any mrec_tmp; 68*17dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 69243ca327SMatthew Dillon hammer_tid_t sync_tid; 70a7fbbf91SMatthew Dillon const char *filesystem; 71a7fbbf91SMatthew Dillon char *buf = malloc(SERIALBUF_SIZE); 72243ca327SMatthew Dillon int interrupted = 0; 73243ca327SMatthew Dillon int error; 74a7fbbf91SMatthew Dillon int fd; 75243ca327SMatthew Dillon int n; 76243ca327SMatthew Dillon time_t base_t = time(NULL); 77a7fbbf91SMatthew Dillon 78a7fbbf91SMatthew Dillon if (ac > 2) 79a7fbbf91SMatthew Dillon mirror_usage(1); 80a7fbbf91SMatthew Dillon filesystem = av[0]; 81a7fbbf91SMatthew Dillon 82a7fbbf91SMatthew Dillon bzero(&mirror, sizeof(mirror)); 83a7fbbf91SMatthew Dillon hammer_key_beg_init(&mirror.key_beg); 84a7fbbf91SMatthew Dillon hammer_key_end_init(&mirror.key_end); 85a7fbbf91SMatthew Dillon 86d4e5b69bSMatthew Dillon fd = getpfs(&pfs, filesystem); 87a7fbbf91SMatthew Dillon 88243ca327SMatthew Dillon /* 89d4e5b69bSMatthew Dillon * In 2-way mode the target will send us a PFS info packet 90d4e5b69bSMatthew Dillon * first. Use the target's current snapshot TID as our default 91d4e5b69bSMatthew Dillon * begin TID. 92243ca327SMatthew Dillon */ 93d4e5b69bSMatthew Dillon mirror.tid_beg = 0; 94d4e5b69bSMatthew Dillon if (TwoWayPipeOpt) 95d4e5b69bSMatthew Dillon validate_mrec_header(fd, 0, 0, pfs.pfs_id, 96d4e5b69bSMatthew Dillon NULL, &mirror.tid_beg); 97d4e5b69bSMatthew Dillon 98d4e5b69bSMatthew Dillon /* 99d4e5b69bSMatthew Dillon * Write out the PFS header, tid_beg will be updated if our PFS 100d4e5b69bSMatthew Dillon * has a larger begin sync. tid_end is set to the latest source 101d4e5b69bSMatthew Dillon * TID whos flush cycle has completed. 102d4e5b69bSMatthew Dillon */ 103d4e5b69bSMatthew Dillon generate_mrec_header(fd, 1, pfs.pfs_id, 104d4e5b69bSMatthew Dillon &mirror.tid_beg, &mirror.tid_end); 105d4e5b69bSMatthew Dillon 106d4e5b69bSMatthew Dillon /* 107d4e5b69bSMatthew Dillon * A cycle file overrides the beginning TID 108d4e5b69bSMatthew Dillon */ 109243ca327SMatthew Dillon hammer_get_cycle(&mirror.key_beg, &mirror.tid_beg); 11034ebae70SMatthew Dillon 111*17dd83bcSMatthew Dillon if (ac == 2) 112*17dd83bcSMatthew Dillon mirror.tid_beg = strtoull(av[1], NULL, 0); 113*17dd83bcSMatthew Dillon 114243ca327SMatthew Dillon fprintf(stderr, "mirror-read: Mirror from %016llx to %016llx\n", 115243ca327SMatthew Dillon mirror.tid_beg, mirror.tid_end); 116243ca327SMatthew Dillon if (mirror.key_beg.obj_id != (int64_t)HAMMER_MIN_OBJID) { 117243ca327SMatthew Dillon fprintf(stderr, "mirror-read: Resuming at object %016llx\n", 118243ca327SMatthew Dillon mirror.key_beg.obj_id); 119243ca327SMatthew Dillon } 120243ca327SMatthew Dillon 121243ca327SMatthew Dillon /* 122243ca327SMatthew Dillon * Write out bulk records 123243ca327SMatthew Dillon */ 124a7fbbf91SMatthew Dillon mirror.ubuf = buf; 125a7fbbf91SMatthew Dillon mirror.size = SERIALBUF_SIZE; 126a7fbbf91SMatthew Dillon 127a7fbbf91SMatthew Dillon do { 128a7fbbf91SMatthew Dillon mirror.count = 0; 129d4e5b69bSMatthew Dillon mirror.pfs_id = pfs.pfs_id; 130d4e5b69bSMatthew Dillon mirror.shared_uuid = pfs.ondisk->shared_uuid; 131a7fbbf91SMatthew Dillon if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) { 132a7fbbf91SMatthew Dillon fprintf(stderr, "Mirror-read %s failed: %s\n", 133a7fbbf91SMatthew Dillon filesystem, strerror(errno)); 134a7fbbf91SMatthew Dillon exit(1); 135a7fbbf91SMatthew Dillon } 136243ca327SMatthew Dillon if (mirror.count) { 137243ca327SMatthew Dillon n = write(1, mirror.ubuf, mirror.count); 138243ca327SMatthew Dillon if (n != mirror.count) { 139243ca327SMatthew Dillon fprintf(stderr, "Mirror-read %s failed: " 140243ca327SMatthew Dillon "short write\n", 141243ca327SMatthew Dillon filesystem); 142243ca327SMatthew Dillon exit(1); 143243ca327SMatthew Dillon } 144a7fbbf91SMatthew Dillon } 145a7fbbf91SMatthew Dillon mirror.key_beg = mirror.key_cur; 146243ca327SMatthew Dillon if (TimeoutOpt && 147243ca327SMatthew Dillon (unsigned)(time(NULL) - base_t) > (unsigned)TimeoutOpt) { 148243ca327SMatthew Dillon fprintf(stderr, 149243ca327SMatthew Dillon "Mirror-read %s interrupted by timer at" 150243ca327SMatthew Dillon " %016llx\n", 151243ca327SMatthew Dillon filesystem, 152243ca327SMatthew Dillon mirror.key_cur.obj_id); 153243ca327SMatthew Dillon interrupted = 1; 154243ca327SMatthew Dillon break; 155243ca327SMatthew Dillon } 156a7fbbf91SMatthew Dillon } while (mirror.count != 0); 157a7fbbf91SMatthew Dillon 158243ca327SMatthew Dillon /* 159243ca327SMatthew Dillon * Write out the termination sync record 160243ca327SMatthew Dillon */ 161*17dd83bcSMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_SYNC, 162*17dd83bcSMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.sync)); 16334ebae70SMatthew Dillon 164243ca327SMatthew Dillon /* 165243ca327SMatthew Dillon * If the -2 option was given (automatic when doing mirror-copy), 166243ca327SMatthew Dillon * a two-way pipe is assumed and we expect a response mrec from 167243ca327SMatthew Dillon * the target. 168243ca327SMatthew Dillon */ 169243ca327SMatthew Dillon if (TwoWayPipeOpt) { 170243ca327SMatthew Dillon mrec = read_mrecord(0, &error, NULL); 171*17dd83bcSMatthew Dillon if (mrec == NULL || 172*17dd83bcSMatthew Dillon mrec->head.type != HAMMER_MREC_TYPE_UPDATE || 173*17dd83bcSMatthew Dillon mrec->head.rec_size != sizeof(mrec->update)) { 174243ca327SMatthew Dillon fprintf(stderr, "mirror_read: Did not get final " 175243ca327SMatthew Dillon "acknowledgement packet from target\n"); 176243ca327SMatthew Dillon exit(1); 177243ca327SMatthew Dillon } 178243ca327SMatthew Dillon if (interrupted) { 179243ca327SMatthew Dillon if (CyclePath) { 180243ca327SMatthew Dillon hammer_set_cycle(&mirror.key_cur, mirror.tid_beg); 181243ca327SMatthew Dillon fprintf(stderr, "Cyclefile %s updated for continuation\n", CyclePath); 182243ca327SMatthew Dillon } 183243ca327SMatthew Dillon } else { 184*17dd83bcSMatthew Dillon sync_tid = mrec->update.tid; 185243ca327SMatthew Dillon if (CyclePath) { 186243ca327SMatthew Dillon hammer_key_beg_init(&mirror.key_beg); 187243ca327SMatthew Dillon hammer_set_cycle(&mirror.key_beg, sync_tid); 188243ca327SMatthew Dillon fprintf(stderr, "Cyclefile %s updated to 0x%016llx\n", 189243ca327SMatthew Dillon CyclePath, sync_tid); 190243ca327SMatthew Dillon } else { 191243ca327SMatthew Dillon fprintf(stderr, "Source can update synctid " 192243ca327SMatthew Dillon "to 0x%016llx\n", 193243ca327SMatthew Dillon sync_tid); 194243ca327SMatthew Dillon } 195243ca327SMatthew Dillon } 196243ca327SMatthew Dillon } else if (CyclePath) { 197243ca327SMatthew Dillon /* NOTE! mirror.tid_beg cannot be updated */ 198243ca327SMatthew Dillon fprintf(stderr, "Warning: cycle file (-c option) cannot be " 199243ca327SMatthew Dillon "fully updated unless you use mirror-copy\n"); 200243ca327SMatthew Dillon hammer_set_cycle(&mirror.key_beg, mirror.tid_beg); 201243ca327SMatthew Dillon } 202a7fbbf91SMatthew Dillon fprintf(stderr, "Mirror-read %s succeeded\n", filesystem); 203a7fbbf91SMatthew Dillon } 204a7fbbf91SMatthew Dillon 205*17dd83bcSMatthew Dillon /* 206*17dd83bcSMatthew Dillon * Pipe the mirroring data stream on stdin to the HAMMER VFS, adding 207*17dd83bcSMatthew Dillon * some additional packet types to negotiate TID ranges and to verify 208*17dd83bcSMatthew Dillon * completion. The HAMMER VFS does most of the work. 209*17dd83bcSMatthew Dillon * 210*17dd83bcSMatthew Dillon * It is important to note that the mirror.key_{beg,end} range must 211*17dd83bcSMatthew Dillon * match the ranged used by the original. For now both sides use 212*17dd83bcSMatthew Dillon * range the entire key space. 213*17dd83bcSMatthew Dillon * 214*17dd83bcSMatthew Dillon * It is even more important that the records in the stream conform 215*17dd83bcSMatthew Dillon * to the TID range also supplied in the stream. The HAMMER VFS will 216*17dd83bcSMatthew Dillon * use the REC, PASS, and SKIP record types to track the portions of 217*17dd83bcSMatthew Dillon * the B-Tree being scanned in order to be able to proactively delete 218*17dd83bcSMatthew Dillon * records on the target within those active areas that are not mentioned 219*17dd83bcSMatthew Dillon * by the source. 220*17dd83bcSMatthew Dillon * 221*17dd83bcSMatthew Dillon * The mirror.key_cur field is used by the VFS to do this tracking. It 222*17dd83bcSMatthew Dillon * must be initialized to key_beg but then is persistently updated by 223*17dd83bcSMatthew Dillon * the HAMMER VFS on each successive ioctl() call. If you blow up this 224*17dd83bcSMatthew Dillon * field you will blow up the mirror target, possibly to the point of 225*17dd83bcSMatthew Dillon * deleting everything. As a safety measure the HAMMER VFS simply marks 226*17dd83bcSMatthew Dillon * the records that the source has destroyed as deleted on the target, 227*17dd83bcSMatthew Dillon * and normal pruning operations will deal with their final disposition 228*17dd83bcSMatthew Dillon * at some later time. 229*17dd83bcSMatthew Dillon */ 230a7fbbf91SMatthew Dillon void 231a7fbbf91SMatthew Dillon hammer_cmd_mirror_write(char **av, int ac) 232a7fbbf91SMatthew Dillon { 233a7fbbf91SMatthew Dillon struct hammer_ioc_mirror_rw mirror; 234a7fbbf91SMatthew Dillon const char *filesystem; 235a7fbbf91SMatthew Dillon char *buf = malloc(SERIALBUF_SIZE); 236d4e5b69bSMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 237*17dd83bcSMatthew Dillon struct hammer_ioc_mrecord_head pickup; 238243ca327SMatthew Dillon struct hammer_ioc_synctid synctid; 239*17dd83bcSMatthew Dillon union hammer_ioc_mrecord_any mrec_tmp; 240*17dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 241243ca327SMatthew Dillon int error; 242243ca327SMatthew Dillon int fd; 243a7fbbf91SMatthew Dillon 244a7fbbf91SMatthew Dillon if (ac > 2) 245a7fbbf91SMatthew Dillon mirror_usage(1); 246a7fbbf91SMatthew Dillon filesystem = av[0]; 247a7fbbf91SMatthew Dillon 248a7fbbf91SMatthew Dillon bzero(&mirror, sizeof(mirror)); 249a7fbbf91SMatthew Dillon hammer_key_beg_init(&mirror.key_beg); 250a7fbbf91SMatthew Dillon hammer_key_end_init(&mirror.key_end); 251*17dd83bcSMatthew Dillon mirror.key_end = mirror.key_beg; 252a7fbbf91SMatthew Dillon 253d4e5b69bSMatthew Dillon fd = getpfs(&pfs, filesystem); 254a7fbbf91SMatthew Dillon 255243ca327SMatthew Dillon /* 256d4e5b69bSMatthew Dillon * In two-way mode the target writes out a PFS packet first. 257d4e5b69bSMatthew Dillon * The source uses our tid_end as its tid_beg by default, 258d4e5b69bSMatthew Dillon * picking up where it left off. 259243ca327SMatthew Dillon */ 260d4e5b69bSMatthew Dillon mirror.tid_beg = 0; 261d4e5b69bSMatthew Dillon if (TwoWayPipeOpt) { 262d4e5b69bSMatthew Dillon generate_mrec_header(fd, 1, pfs.pfs_id, 263d4e5b69bSMatthew Dillon &mirror.tid_beg, &mirror.tid_end); 264d4e5b69bSMatthew Dillon } 265d4e5b69bSMatthew Dillon 266d4e5b69bSMatthew Dillon /* 267*17dd83bcSMatthew Dillon * Read and process the PFS header. The source informs us of 268*17dd83bcSMatthew Dillon * the TID range the stream represents. 269d4e5b69bSMatthew Dillon */ 270d4e5b69bSMatthew Dillon validate_mrec_header(fd, 0, 1, pfs.pfs_id, 271d4e5b69bSMatthew Dillon &mirror.tid_beg, &mirror.tid_end); 27234ebae70SMatthew Dillon 273a7fbbf91SMatthew Dillon mirror.ubuf = buf; 274a7fbbf91SMatthew Dillon mirror.size = SERIALBUF_SIZE; 275a7fbbf91SMatthew Dillon 276a7fbbf91SMatthew Dillon pickup.signature = 0; 277243ca327SMatthew Dillon pickup.type = 0; 278a7fbbf91SMatthew Dillon 279243ca327SMatthew Dillon /* 280*17dd83bcSMatthew Dillon * Read and process bulk records (REC, PASS, and SKIP types). 281*17dd83bcSMatthew Dillon * 282*17dd83bcSMatthew Dillon * On your life, do NOT mess with mirror.key_cur or your mirror 283*17dd83bcSMatthew Dillon * target may become history. 284243ca327SMatthew Dillon */ 285a7fbbf91SMatthew Dillon for (;;) { 286a7fbbf91SMatthew Dillon mirror.count = 0; 287d4e5b69bSMatthew Dillon mirror.pfs_id = pfs.pfs_id; 288d4e5b69bSMatthew Dillon mirror.shared_uuid = pfs.ondisk->shared_uuid; 289a7fbbf91SMatthew Dillon mirror.size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup); 290a7fbbf91SMatthew Dillon if (mirror.size <= 0) 291a7fbbf91SMatthew Dillon break; 292a7fbbf91SMatthew Dillon if (ioctl(fd, HAMMERIOC_MIRROR_WRITE, &mirror) < 0) { 293a7fbbf91SMatthew Dillon fprintf(stderr, "Mirror-write %s failed: %s\n", 294a7fbbf91SMatthew Dillon filesystem, strerror(errno)); 295a7fbbf91SMatthew Dillon exit(1); 296a7fbbf91SMatthew Dillon } 297*17dd83bcSMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) { 298*17dd83bcSMatthew Dillon fprintf(stderr, 299*17dd83bcSMatthew Dillon "Mirror-write %s fatal error %d\n", 300*17dd83bcSMatthew Dillon filesystem, mirror.head.error); 301*17dd83bcSMatthew Dillon exit(1); 302*17dd83bcSMatthew Dillon } 303243ca327SMatthew Dillon #if 0 304a7fbbf91SMatthew Dillon if (mirror.head.flags & HAMMER_IOC_HEAD_INTR) { 305a7fbbf91SMatthew Dillon fprintf(stderr, 306a7fbbf91SMatthew Dillon "Mirror-write %s interrupted by timer at" 307243ca327SMatthew Dillon " %016llx\n", 308a7fbbf91SMatthew Dillon filesystem, 309243ca327SMatthew Dillon mirror.key_cur.obj_id); 310a7fbbf91SMatthew Dillon exit(0); 311a7fbbf91SMatthew Dillon } 312243ca327SMatthew Dillon #endif 313a7fbbf91SMatthew Dillon } 314243ca327SMatthew Dillon 315243ca327SMatthew Dillon /* 316243ca327SMatthew Dillon * Read and process the termination sync record. 317243ca327SMatthew Dillon */ 318243ca327SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 319*17dd83bcSMatthew Dillon if (mrec == NULL || 320*17dd83bcSMatthew Dillon mrec->head.type != HAMMER_MREC_TYPE_SYNC || 321*17dd83bcSMatthew Dillon mrec->head.rec_size != sizeof(mrec->sync)) { 322243ca327SMatthew Dillon fprintf(stderr, "Mirror-write %s: Did not get termination " 323*17dd83bcSMatthew Dillon "sync record, or rec_size is wrong rt=%d\n", 324*17dd83bcSMatthew Dillon filesystem, mrec->head.type); 325243ca327SMatthew Dillon } 326*17dd83bcSMatthew Dillon free(mrec); 327*17dd83bcSMatthew Dillon mrec = NULL; 328243ca327SMatthew Dillon 329243ca327SMatthew Dillon /* 330243ca327SMatthew Dillon * Update the PFS info on the target so the user has visibility 331243ca327SMatthew Dillon * into the new snapshot. 332243ca327SMatthew Dillon */ 333d4e5b69bSMatthew Dillon update_pfs_snapshot(fd, mirror.tid_end, pfs.pfs_id); 334243ca327SMatthew Dillon 335243ca327SMatthew Dillon /* 336243ca327SMatthew Dillon * Sync the target filesystem 337243ca327SMatthew Dillon */ 338243ca327SMatthew Dillon bzero(&synctid, sizeof(synctid)); 339243ca327SMatthew Dillon synctid.op = HAMMER_SYNCTID_SYNC2; 340243ca327SMatthew Dillon ioctl(fd, HAMMERIOC_SYNCTID, &synctid); 341243ca327SMatthew Dillon 342243ca327SMatthew Dillon fprintf(stderr, "Mirror-write %s: succeeded\n", filesystem); 343243ca327SMatthew Dillon 344243ca327SMatthew Dillon /* 345243ca327SMatthew Dillon * Report back to the originator. 346243ca327SMatthew Dillon */ 347243ca327SMatthew Dillon if (TwoWayPipeOpt) { 348*17dd83bcSMatthew Dillon mrec_tmp.update.tid = mirror.tid_end; 349243ca327SMatthew Dillon write_mrecord(1, HAMMER_MREC_TYPE_UPDATE, 350*17dd83bcSMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.update)); 351243ca327SMatthew Dillon } else { 352243ca327SMatthew Dillon printf("Source can update synctid to 0x%016llx\n", 353243ca327SMatthew Dillon mirror.tid_end); 354243ca327SMatthew Dillon } 355243ca327SMatthew Dillon } 356243ca327SMatthew Dillon 357243ca327SMatthew Dillon void 358243ca327SMatthew Dillon hammer_cmd_mirror_dump(void) 359243ca327SMatthew Dillon { 360243ca327SMatthew Dillon char *buf = malloc(SERIALBUF_SIZE); 361*17dd83bcSMatthew Dillon struct hammer_ioc_mrecord_head pickup; 362*17dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 363243ca327SMatthew Dillon int error; 364243ca327SMatthew Dillon int size; 365*17dd83bcSMatthew Dillon int offset; 366*17dd83bcSMatthew Dillon int bytes; 367243ca327SMatthew Dillon 368243ca327SMatthew Dillon /* 369243ca327SMatthew Dillon * Read and process the PFS header 370243ca327SMatthew Dillon */ 371243ca327SMatthew Dillon pickup.signature = 0; 372243ca327SMatthew Dillon pickup.type = 0; 373243ca327SMatthew Dillon 374243ca327SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 375243ca327SMatthew Dillon 376243ca327SMatthew Dillon /* 377243ca327SMatthew Dillon * Read and process bulk records 378243ca327SMatthew Dillon */ 379243ca327SMatthew Dillon for (;;) { 380243ca327SMatthew Dillon size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup); 381243ca327SMatthew Dillon if (size <= 0) 382243ca327SMatthew Dillon break; 383*17dd83bcSMatthew Dillon offset = 0; 384*17dd83bcSMatthew Dillon while (offset < size) { 385*17dd83bcSMatthew Dillon mrec = (void *)((char *)buf + offset); 386*17dd83bcSMatthew Dillon bytes = HAMMER_HEAD_DOALIGN(mrec->head.rec_size); 387*17dd83bcSMatthew Dillon if (offset + bytes > size) { 388*17dd83bcSMatthew Dillon fprintf(stderr, "Misaligned record\n"); 389*17dd83bcSMatthew Dillon exit(1); 390*17dd83bcSMatthew Dillon } 391*17dd83bcSMatthew Dillon 392*17dd83bcSMatthew Dillon switch(mrec->head.type) { 393*17dd83bcSMatthew Dillon case HAMMER_MREC_TYPE_REC: 394243ca327SMatthew Dillon printf("Record obj=%016llx key=%016llx " 395243ca327SMatthew Dillon "rt=%02x ot=%02x\n", 396*17dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_id, 397*17dd83bcSMatthew Dillon mrec->rec.leaf.base.key, 398*17dd83bcSMatthew Dillon mrec->rec.leaf.base.rec_type, 399*17dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_type); 400243ca327SMatthew Dillon printf(" tids %016llx:%016llx data=%d\n", 401*17dd83bcSMatthew Dillon mrec->rec.leaf.base.create_tid, 402*17dd83bcSMatthew Dillon mrec->rec.leaf.base.delete_tid, 403*17dd83bcSMatthew Dillon mrec->rec.leaf.data_len); 404*17dd83bcSMatthew Dillon break; 405*17dd83bcSMatthew Dillon case HAMMER_MREC_TYPE_PASS: 406*17dd83bcSMatthew Dillon printf("Pass obj=%016llx key=%016llx " 407*17dd83bcSMatthew Dillon "rt=%02x ot=%02x\n", 408*17dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_id, 409*17dd83bcSMatthew Dillon mrec->rec.leaf.base.key, 410*17dd83bcSMatthew Dillon mrec->rec.leaf.base.rec_type, 411*17dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_type); 412*17dd83bcSMatthew Dillon printf(" tids %016llx:%016llx data=%d\n", 413*17dd83bcSMatthew Dillon mrec->rec.leaf.base.create_tid, 414*17dd83bcSMatthew Dillon mrec->rec.leaf.base.delete_tid, 415*17dd83bcSMatthew Dillon mrec->rec.leaf.data_len); 416*17dd83bcSMatthew Dillon break; 417*17dd83bcSMatthew Dillon case HAMMER_MREC_TYPE_SKIP: 418*17dd83bcSMatthew Dillon printf("Skip obj=%016llx key=%016llx rt=%02x to\n" 419*17dd83bcSMatthew Dillon " obj=%016llx key=%016llx rt=%02x\n", 420*17dd83bcSMatthew Dillon mrec->skip.skip_beg.obj_id, 421*17dd83bcSMatthew Dillon mrec->skip.skip_beg.key, 422*17dd83bcSMatthew Dillon mrec->skip.skip_beg.rec_type, 423*17dd83bcSMatthew Dillon mrec->skip.skip_end.obj_id, 424*17dd83bcSMatthew Dillon mrec->skip.skip_end.key, 425*17dd83bcSMatthew Dillon mrec->skip.skip_end.rec_type); 426*17dd83bcSMatthew Dillon default: 427*17dd83bcSMatthew Dillon break; 428*17dd83bcSMatthew Dillon } 429*17dd83bcSMatthew Dillon offset += bytes; 430243ca327SMatthew Dillon } 431243ca327SMatthew Dillon } 432243ca327SMatthew Dillon 433243ca327SMatthew Dillon /* 434243ca327SMatthew Dillon * Read and process the termination sync record. 435243ca327SMatthew Dillon */ 436243ca327SMatthew Dillon mrec = read_mrecord(0, &error, &pickup); 437*17dd83bcSMatthew Dillon if (mrec == NULL || mrec->head.type != HAMMER_MREC_TYPE_SYNC) { 438243ca327SMatthew Dillon fprintf(stderr, "Mirror-dump: Did not get termination " 439243ca327SMatthew Dillon "sync record\n"); 440243ca327SMatthew Dillon } 441a7fbbf91SMatthew Dillon } 442a7fbbf91SMatthew Dillon 443a7fbbf91SMatthew Dillon void 444a7fbbf91SMatthew Dillon hammer_cmd_mirror_copy(char **av, int ac) 445a7fbbf91SMatthew Dillon { 44634ebae70SMatthew Dillon pid_t pid1; 44734ebae70SMatthew Dillon pid_t pid2; 44834ebae70SMatthew Dillon int fds[2]; 449243ca327SMatthew Dillon const char *xav[16]; 450243ca327SMatthew Dillon char tbuf[16]; 45134ebae70SMatthew Dillon char *ptr; 452243ca327SMatthew Dillon int xac; 45334ebae70SMatthew Dillon 45434ebae70SMatthew Dillon if (ac != 2) 45534ebae70SMatthew Dillon mirror_usage(1); 45634ebae70SMatthew Dillon 45734ebae70SMatthew Dillon if (pipe(fds) < 0) { 45834ebae70SMatthew Dillon perror("pipe"); 45934ebae70SMatthew Dillon exit(1); 46034ebae70SMatthew Dillon } 46134ebae70SMatthew Dillon 462243ca327SMatthew Dillon TwoWayPipeOpt = 1; 463243ca327SMatthew Dillon 46434ebae70SMatthew Dillon /* 46534ebae70SMatthew Dillon * Source 46634ebae70SMatthew Dillon */ 46734ebae70SMatthew Dillon if ((pid1 = fork()) == 0) { 46834ebae70SMatthew Dillon dup2(fds[0], 0); 46934ebae70SMatthew Dillon dup2(fds[0], 1); 47034ebae70SMatthew Dillon close(fds[0]); 47134ebae70SMatthew Dillon close(fds[1]); 47234ebae70SMatthew Dillon if ((ptr = strchr(av[0], ':')) != NULL) { 47334ebae70SMatthew Dillon *ptr++ = 0; 474243ca327SMatthew Dillon xac = 0; 475243ca327SMatthew Dillon xav[xac++] = "ssh"; 476243ca327SMatthew Dillon xav[xac++] = av[0]; 477243ca327SMatthew Dillon xav[xac++] = "hammer"; 478243ca327SMatthew Dillon if (VerboseOpt) 479243ca327SMatthew Dillon xav[xac++] = "-v"; 480243ca327SMatthew Dillon xav[xac++] = "-2"; 481243ca327SMatthew Dillon if (TimeoutOpt) { 482243ca327SMatthew Dillon snprintf(tbuf, sizeof(tbuf), "%d", TimeoutOpt); 483243ca327SMatthew Dillon xav[xac++] = "-t"; 484243ca327SMatthew Dillon xav[xac++] = tbuf; 485243ca327SMatthew Dillon } 486243ca327SMatthew Dillon xav[xac++] = "mirror-read"; 487243ca327SMatthew Dillon xav[xac++] = ptr; 488243ca327SMatthew Dillon xav[xac++] = NULL; 489243ca327SMatthew Dillon execv("/usr/bin/ssh", (void *)xav); 49034ebae70SMatthew Dillon } else { 49134ebae70SMatthew Dillon hammer_cmd_mirror_read(av, 1); 492243ca327SMatthew Dillon fflush(stdout); 493243ca327SMatthew Dillon fflush(stderr); 49434ebae70SMatthew Dillon } 49553d93cc7SMatthew Dillon _exit(1); 49634ebae70SMatthew Dillon } 49734ebae70SMatthew Dillon 49834ebae70SMatthew Dillon /* 49934ebae70SMatthew Dillon * Target 50034ebae70SMatthew Dillon */ 50134ebae70SMatthew Dillon if ((pid2 = fork()) == 0) { 50234ebae70SMatthew Dillon dup2(fds[1], 0); 50334ebae70SMatthew Dillon dup2(fds[1], 1); 50434ebae70SMatthew Dillon close(fds[0]); 50534ebae70SMatthew Dillon close(fds[1]); 50634ebae70SMatthew Dillon if ((ptr = strchr(av[1], ':')) != NULL) { 50734ebae70SMatthew Dillon *ptr++ = 0; 508243ca327SMatthew Dillon xac = 0; 509243ca327SMatthew Dillon xav[xac++] = "ssh"; 510243ca327SMatthew Dillon xav[xac++] = av[1]; 511243ca327SMatthew Dillon xav[xac++] = "hammer"; 512243ca327SMatthew Dillon if (VerboseOpt) 513243ca327SMatthew Dillon xav[xac++] = "-v"; 514243ca327SMatthew Dillon xav[xac++] = "-2"; 515243ca327SMatthew Dillon xav[xac++] = "mirror-write"; 516243ca327SMatthew Dillon xav[xac++] = ptr; 517243ca327SMatthew Dillon xav[xac++] = NULL; 518243ca327SMatthew Dillon execv("/usr/bin/ssh", (void *)xav); 51934ebae70SMatthew Dillon } else { 52034ebae70SMatthew Dillon hammer_cmd_mirror_write(av + 1, 1); 521243ca327SMatthew Dillon fflush(stdout); 522243ca327SMatthew Dillon fflush(stderr); 52334ebae70SMatthew Dillon } 52453d93cc7SMatthew Dillon _exit(1); 52534ebae70SMatthew Dillon } 52634ebae70SMatthew Dillon close(fds[0]); 52734ebae70SMatthew Dillon close(fds[1]); 52834ebae70SMatthew Dillon 52934ebae70SMatthew Dillon while (waitpid(pid1, NULL, 0) <= 0) 53034ebae70SMatthew Dillon ; 53134ebae70SMatthew Dillon while (waitpid(pid2, NULL, 0) <= 0) 53234ebae70SMatthew Dillon ; 533a7fbbf91SMatthew Dillon } 534a7fbbf91SMatthew Dillon 535243ca327SMatthew Dillon /* 536243ca327SMatthew Dillon * Read and return multiple mrecords 537243ca327SMatthew Dillon */ 538a7fbbf91SMatthew Dillon static int 539*17dd83bcSMatthew Dillon read_mrecords(int fd, char *buf, u_int size, hammer_ioc_mrecord_head_t pickup) 540a7fbbf91SMatthew Dillon { 541*17dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 542a7fbbf91SMatthew Dillon u_int count; 543a7fbbf91SMatthew Dillon size_t n; 544a7fbbf91SMatthew Dillon size_t i; 545*17dd83bcSMatthew Dillon size_t bytes; 546a7fbbf91SMatthew Dillon 547a7fbbf91SMatthew Dillon count = 0; 548a7fbbf91SMatthew Dillon while (size - count >= HAMMER_MREC_HEADSIZE) { 549a7fbbf91SMatthew Dillon /* 550a7fbbf91SMatthew Dillon * Cached the record header in case we run out of buffer 551a7fbbf91SMatthew Dillon * space. 552a7fbbf91SMatthew Dillon */ 553*17dd83bcSMatthew Dillon fflush(stdout); 554a7fbbf91SMatthew Dillon if (pickup->signature == 0) { 555a7fbbf91SMatthew Dillon for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) { 556a7fbbf91SMatthew Dillon i = read(fd, (char *)pickup + n, 557a7fbbf91SMatthew Dillon HAMMER_MREC_HEADSIZE - n); 558a7fbbf91SMatthew Dillon if (i <= 0) 559a7fbbf91SMatthew Dillon break; 560a7fbbf91SMatthew Dillon } 561a7fbbf91SMatthew Dillon if (n == 0) 562a7fbbf91SMatthew Dillon break; 563a7fbbf91SMatthew Dillon if (n != HAMMER_MREC_HEADSIZE) { 564a7fbbf91SMatthew Dillon fprintf(stderr, "read_mrecords: short read on pipe\n"); 565a7fbbf91SMatthew Dillon exit(1); 566a7fbbf91SMatthew Dillon } 567a7fbbf91SMatthew Dillon 568a7fbbf91SMatthew Dillon if (pickup->signature != HAMMER_IOC_MIRROR_SIGNATURE) { 569a7fbbf91SMatthew Dillon fprintf(stderr, "read_mrecords: malformed record on pipe, bad signature\n"); 570a7fbbf91SMatthew Dillon exit(1); 571a7fbbf91SMatthew Dillon } 572a7fbbf91SMatthew Dillon } 573a7fbbf91SMatthew Dillon if (pickup->rec_size < HAMMER_MREC_HEADSIZE || 574*17dd83bcSMatthew Dillon pickup->rec_size > sizeof(*mrec) + HAMMER_XBUFSIZE) { 575a7fbbf91SMatthew Dillon fprintf(stderr, "read_mrecords: malformed record on pipe, illegal rec_size\n"); 576a7fbbf91SMatthew Dillon exit(1); 577a7fbbf91SMatthew Dillon } 578a7fbbf91SMatthew Dillon 579a7fbbf91SMatthew Dillon /* 580a7fbbf91SMatthew Dillon * Stop if we have insufficient space for the record and data. 581a7fbbf91SMatthew Dillon */ 582*17dd83bcSMatthew Dillon bytes = HAMMER_HEAD_DOALIGN(pickup->rec_size); 583*17dd83bcSMatthew Dillon if (size - count < bytes) 584a7fbbf91SMatthew Dillon break; 585a7fbbf91SMatthew Dillon 586a7fbbf91SMatthew Dillon /* 587*17dd83bcSMatthew Dillon * Stop if the record type is not a REC or a SKIP (the only 588*17dd83bcSMatthew Dillon * two types the ioctl supports. Other types are used only 589*17dd83bcSMatthew Dillon * by the userland protocol). 590243ca327SMatthew Dillon */ 591*17dd83bcSMatthew Dillon if (pickup->type != HAMMER_MREC_TYPE_REC && 592*17dd83bcSMatthew Dillon pickup->type != HAMMER_MREC_TYPE_SKIP && 593*17dd83bcSMatthew Dillon pickup->type != HAMMER_MREC_TYPE_PASS) { 594243ca327SMatthew Dillon break; 595*17dd83bcSMatthew Dillon } 596243ca327SMatthew Dillon 597243ca327SMatthew Dillon /* 598a7fbbf91SMatthew Dillon * Read the remainder and clear the pickup signature. 599a7fbbf91SMatthew Dillon */ 600*17dd83bcSMatthew Dillon for (n = HAMMER_MREC_HEADSIZE; n < bytes; n += i) { 601*17dd83bcSMatthew Dillon i = read(fd, buf + count + n, bytes - n); 602a7fbbf91SMatthew Dillon if (i <= 0) 603a7fbbf91SMatthew Dillon break; 604a7fbbf91SMatthew Dillon } 605*17dd83bcSMatthew Dillon if (n != bytes) { 606a7fbbf91SMatthew Dillon fprintf(stderr, "read_mrecords: short read on pipe\n"); 607a7fbbf91SMatthew Dillon exit(1); 608a7fbbf91SMatthew Dillon } 609*17dd83bcSMatthew Dillon 610*17dd83bcSMatthew Dillon bcopy(pickup, buf + count, HAMMER_MREC_HEADSIZE); 611*17dd83bcSMatthew Dillon pickup->signature = 0; 612*17dd83bcSMatthew Dillon pickup->type = 0; 613*17dd83bcSMatthew Dillon mrec = (void *)(buf + count); 614*17dd83bcSMatthew Dillon 615*17dd83bcSMatthew Dillon /* 616*17dd83bcSMatthew Dillon * Validate the completed record 617*17dd83bcSMatthew Dillon */ 618*17dd83bcSMatthew Dillon if (mrec->head.rec_crc != 619*17dd83bcSMatthew Dillon crc32((char *)mrec + HAMMER_MREC_CRCOFF, 620*17dd83bcSMatthew Dillon mrec->head.rec_size - HAMMER_MREC_CRCOFF)) { 621*17dd83bcSMatthew Dillon fprintf(stderr, "read_mrecords: malformed record " 622*17dd83bcSMatthew Dillon "on pipe, bad crc\n"); 623*17dd83bcSMatthew Dillon exit(1); 624a7fbbf91SMatthew Dillon } 625a7fbbf91SMatthew Dillon 626*17dd83bcSMatthew Dillon /* 627*17dd83bcSMatthew Dillon * If its a B-Tree record validate the data crc 628*17dd83bcSMatthew Dillon */ 629*17dd83bcSMatthew Dillon if (mrec->head.type == HAMMER_MREC_TYPE_REC) { 630*17dd83bcSMatthew Dillon if (mrec->head.rec_size < 631*17dd83bcSMatthew Dillon sizeof(mrec->rec) + mrec->rec.leaf.data_len) { 632*17dd83bcSMatthew Dillon fprintf(stderr, 633*17dd83bcSMatthew Dillon "read_mrecords: malformed record on " 634*17dd83bcSMatthew Dillon "pipe, illegal element data_len\n"); 635*17dd83bcSMatthew Dillon exit(1); 636*17dd83bcSMatthew Dillon } 637*17dd83bcSMatthew Dillon if (mrec->rec.leaf.data_len && 638*17dd83bcSMatthew Dillon mrec->rec.leaf.data_offset && 639*17dd83bcSMatthew Dillon hammer_crc_test_leaf(&mrec->rec + 1, &mrec->rec.leaf) == 0) { 640*17dd83bcSMatthew Dillon fprintf(stderr, 641*17dd83bcSMatthew Dillon "read_mrecords: data_crc did not " 642*17dd83bcSMatthew Dillon "match data! obj=%016llx key=%016llx\n", 643*17dd83bcSMatthew Dillon mrec->rec.leaf.base.obj_id, 644*17dd83bcSMatthew Dillon mrec->rec.leaf.base.key); 645*17dd83bcSMatthew Dillon fprintf(stderr, 646*17dd83bcSMatthew Dillon "continuing, but there are problems\n"); 647*17dd83bcSMatthew Dillon } 648*17dd83bcSMatthew Dillon } 649*17dd83bcSMatthew Dillon count += bytes; 650a7fbbf91SMatthew Dillon } 651a7fbbf91SMatthew Dillon return(count); 652a7fbbf91SMatthew Dillon } 653a7fbbf91SMatthew Dillon 65434ebae70SMatthew Dillon /* 655*17dd83bcSMatthew Dillon * Read and return a single mrecord. 656243ca327SMatthew Dillon */ 657243ca327SMatthew Dillon static 658*17dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t 659*17dd83bcSMatthew Dillon read_mrecord(int fdin, int *errorp, hammer_ioc_mrecord_head_t pickup) 660243ca327SMatthew Dillon { 661*17dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 662*17dd83bcSMatthew Dillon struct hammer_ioc_mrecord_head mrechd; 663243ca327SMatthew Dillon size_t bytes; 664243ca327SMatthew Dillon size_t n; 665243ca327SMatthew Dillon size_t i; 666243ca327SMatthew Dillon 667243ca327SMatthew Dillon if (pickup && pickup->type != 0) { 668243ca327SMatthew Dillon mrechd = *pickup; 669243ca327SMatthew Dillon pickup->signature = 0; 670243ca327SMatthew Dillon pickup->type = 0; 671243ca327SMatthew Dillon n = HAMMER_MREC_HEADSIZE; 672243ca327SMatthew Dillon } else { 673243ca327SMatthew Dillon /* 674243ca327SMatthew Dillon * Read in the PFSD header from the sender. 675243ca327SMatthew Dillon */ 676243ca327SMatthew Dillon for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) { 677243ca327SMatthew Dillon i = read(fdin, (char *)&mrechd + n, HAMMER_MREC_HEADSIZE - n); 678243ca327SMatthew Dillon if (i <= 0) 679243ca327SMatthew Dillon break; 680243ca327SMatthew Dillon } 681243ca327SMatthew Dillon if (n == 0) { 682243ca327SMatthew Dillon *errorp = 0; /* EOF */ 683243ca327SMatthew Dillon return(NULL); 684243ca327SMatthew Dillon } 685243ca327SMatthew Dillon if (n != HAMMER_MREC_HEADSIZE) { 686243ca327SMatthew Dillon fprintf(stderr, "short read of mrecord header\n"); 687243ca327SMatthew Dillon *errorp = EPIPE; 688243ca327SMatthew Dillon return(NULL); 689243ca327SMatthew Dillon } 690243ca327SMatthew Dillon } 691243ca327SMatthew Dillon if (mrechd.signature != HAMMER_IOC_MIRROR_SIGNATURE) { 692243ca327SMatthew Dillon fprintf(stderr, "read_mrecord: bad signature\n"); 693243ca327SMatthew Dillon *errorp = EINVAL; 694243ca327SMatthew Dillon return(NULL); 695243ca327SMatthew Dillon } 696*17dd83bcSMatthew Dillon bytes = HAMMER_HEAD_DOALIGN(mrechd.rec_size); 697*17dd83bcSMatthew Dillon assert(bytes >= sizeof(mrechd)); 698243ca327SMatthew Dillon mrec = malloc(bytes); 699*17dd83bcSMatthew Dillon mrec->head = mrechd; 700*17dd83bcSMatthew Dillon 701243ca327SMatthew Dillon while (n < bytes) { 702243ca327SMatthew Dillon i = read(fdin, (char *)mrec + n, bytes - n); 703243ca327SMatthew Dillon if (i <= 0) 704243ca327SMatthew Dillon break; 705243ca327SMatthew Dillon n += i; 706243ca327SMatthew Dillon } 707243ca327SMatthew Dillon if (n != bytes) { 708243ca327SMatthew Dillon fprintf(stderr, "read_mrecord: short read on payload\n"); 709243ca327SMatthew Dillon *errorp = EPIPE; 710243ca327SMatthew Dillon return(NULL); 711243ca327SMatthew Dillon } 712*17dd83bcSMatthew Dillon if (mrec->head.rec_crc != 713*17dd83bcSMatthew Dillon crc32((char *)mrec + HAMMER_MREC_CRCOFF, 714*17dd83bcSMatthew Dillon mrec->head.rec_size - HAMMER_MREC_CRCOFF)) { 715243ca327SMatthew Dillon fprintf(stderr, "read_mrecord: bad CRC\n"); 716243ca327SMatthew Dillon *errorp = EINVAL; 717243ca327SMatthew Dillon return(NULL); 718243ca327SMatthew Dillon } 719243ca327SMatthew Dillon *errorp = 0; 720243ca327SMatthew Dillon return(mrec); 721243ca327SMatthew Dillon } 722243ca327SMatthew Dillon 723243ca327SMatthew Dillon static 724243ca327SMatthew Dillon void 725*17dd83bcSMatthew Dillon write_mrecord(int fdout, u_int32_t type, hammer_ioc_mrecord_any_t mrec, 726*17dd83bcSMatthew Dillon int bytes) 727243ca327SMatthew Dillon { 728*17dd83bcSMatthew Dillon char zbuf[HAMMER_HEAD_ALIGN]; 729*17dd83bcSMatthew Dillon int pad; 730243ca327SMatthew Dillon 731*17dd83bcSMatthew Dillon pad = HAMMER_HEAD_DOALIGN(bytes) - bytes; 732*17dd83bcSMatthew Dillon 733*17dd83bcSMatthew Dillon assert(bytes >= (int)sizeof(mrec->head)); 734*17dd83bcSMatthew Dillon bzero(&mrec->head, sizeof(mrec->head)); 735*17dd83bcSMatthew Dillon mrec->head.signature = HAMMER_IOC_MIRROR_SIGNATURE; 736*17dd83bcSMatthew Dillon mrec->head.type = type; 737*17dd83bcSMatthew Dillon mrec->head.rec_size = bytes; 738*17dd83bcSMatthew Dillon mrec->head.rec_crc = crc32((char *)mrec + HAMMER_MREC_CRCOFF, 739*17dd83bcSMatthew Dillon bytes - HAMMER_MREC_CRCOFF); 740*17dd83bcSMatthew Dillon if (write(fdout, mrec, bytes) != bytes) { 741243ca327SMatthew Dillon fprintf(stderr, "write_mrecord: error %d (%s)\n", 742243ca327SMatthew Dillon errno, strerror(errno)); 743243ca327SMatthew Dillon exit(1); 744243ca327SMatthew Dillon } 745*17dd83bcSMatthew Dillon if (pad) { 746*17dd83bcSMatthew Dillon bzero(zbuf, pad); 747*17dd83bcSMatthew Dillon if (write(fdout, zbuf, pad) != pad) { 748*17dd83bcSMatthew Dillon fprintf(stderr, "write_mrecord: error %d (%s)\n", 749*17dd83bcSMatthew Dillon errno, strerror(errno)); 750*17dd83bcSMatthew Dillon exit(1); 751*17dd83bcSMatthew Dillon } 752*17dd83bcSMatthew Dillon } 753243ca327SMatthew Dillon } 754243ca327SMatthew Dillon 755243ca327SMatthew Dillon /* 75634ebae70SMatthew Dillon * Generate a mirroring header with the pfs information of the 75734ebae70SMatthew Dillon * originating filesytem. 75834ebae70SMatthew Dillon */ 75934ebae70SMatthew Dillon static void 760d4e5b69bSMatthew Dillon generate_mrec_header(int fd, int fdout, int pfs_id, 76134ebae70SMatthew Dillon hammer_tid_t *tid_begp, hammer_tid_t *tid_endp) 76234ebae70SMatthew Dillon { 76334ebae70SMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 764*17dd83bcSMatthew Dillon union hammer_ioc_mrecord_any mrec_tmp; 76534ebae70SMatthew Dillon 76634ebae70SMatthew Dillon bzero(&pfs, sizeof(pfs)); 767*17dd83bcSMatthew Dillon bzero(&mrec_tmp, sizeof(mrec_tmp)); 768d4e5b69bSMatthew Dillon pfs.pfs_id = pfs_id; 769*17dd83bcSMatthew Dillon pfs.ondisk = &mrec_tmp.pfs.pfsd; 770*17dd83bcSMatthew Dillon pfs.bytes = sizeof(mrec_tmp.pfs.pfsd); 77134ebae70SMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 77234ebae70SMatthew Dillon fprintf(stderr, "mirror-read: not a HAMMER fs/pseudofs!\n"); 77334ebae70SMatthew Dillon exit(1); 77434ebae70SMatthew Dillon } 77534ebae70SMatthew Dillon if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) { 77634ebae70SMatthew Dillon fprintf(stderr, "mirror-read: HAMMER pfs version mismatch!\n"); 77734ebae70SMatthew Dillon exit(1); 77834ebae70SMatthew Dillon } 77934ebae70SMatthew Dillon 78034ebae70SMatthew Dillon /* 78134ebae70SMatthew Dillon * sync_beg_tid - lowest TID on source after which a full history 78234ebae70SMatthew Dillon * is available. 78334ebae70SMatthew Dillon * 78434ebae70SMatthew Dillon * sync_end_tid - highest fully synchronized TID from source. 78534ebae70SMatthew Dillon */ 786*17dd83bcSMatthew Dillon if (tid_begp && *tid_begp < mrec_tmp.pfs.pfsd.sync_beg_tid) 787*17dd83bcSMatthew Dillon *tid_begp = mrec_tmp.pfs.pfsd.sync_beg_tid; 788d4e5b69bSMatthew Dillon if (tid_endp) 789*17dd83bcSMatthew Dillon *tid_endp = mrec_tmp.pfs.pfsd.sync_end_tid; 790*17dd83bcSMatthew Dillon mrec_tmp.pfs.version = pfs.version; 791243ca327SMatthew Dillon write_mrecord(fdout, HAMMER_MREC_TYPE_PFSD, 792*17dd83bcSMatthew Dillon &mrec_tmp, sizeof(mrec_tmp.pfs)); 79334ebae70SMatthew Dillon } 79434ebae70SMatthew Dillon 79534ebae70SMatthew Dillon /* 79634ebae70SMatthew Dillon * Validate the pfs information from the originating filesystem 79734ebae70SMatthew Dillon * against the target filesystem. shared_uuid must match. 79834ebae70SMatthew Dillon */ 79934ebae70SMatthew Dillon static void 800d4e5b69bSMatthew Dillon validate_mrec_header(int fd, int fdin, int is_target, int pfs_id, 80134ebae70SMatthew Dillon hammer_tid_t *tid_begp, hammer_tid_t *tid_endp) 80234ebae70SMatthew Dillon { 80334ebae70SMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 80434ebae70SMatthew Dillon struct hammer_pseudofs_data pfsd; 805*17dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t mrec; 806243ca327SMatthew Dillon int error; 80734ebae70SMatthew Dillon 80834ebae70SMatthew Dillon /* 80934ebae70SMatthew Dillon * Get the PFSD info from the target filesystem. 81034ebae70SMatthew Dillon */ 81134ebae70SMatthew Dillon bzero(&pfs, sizeof(pfs)); 81234ebae70SMatthew Dillon bzero(&pfsd, sizeof(pfsd)); 813d4e5b69bSMatthew Dillon pfs.pfs_id = pfs_id; 81434ebae70SMatthew Dillon pfs.ondisk = &pfsd; 81534ebae70SMatthew Dillon pfs.bytes = sizeof(pfsd); 81634ebae70SMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 81734ebae70SMatthew Dillon fprintf(stderr, "mirror-write: not a HAMMER fs/pseudofs!\n"); 81834ebae70SMatthew Dillon exit(1); 81934ebae70SMatthew Dillon } 82034ebae70SMatthew Dillon if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) { 82134ebae70SMatthew Dillon fprintf(stderr, "mirror-write: HAMMER pfs version mismatch!\n"); 82234ebae70SMatthew Dillon exit(1); 82334ebae70SMatthew Dillon } 82434ebae70SMatthew Dillon 825243ca327SMatthew Dillon mrec = read_mrecord(fdin, &error, NULL); 826243ca327SMatthew Dillon if (mrec == NULL) { 827243ca327SMatthew Dillon if (error == 0) 828243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: short read\n"); 82934ebae70SMatthew Dillon exit(1); 83034ebae70SMatthew Dillon } 831*17dd83bcSMatthew Dillon if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) { 832243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: did not get expected " 833243ca327SMatthew Dillon "PFSD record type\n"); 83434ebae70SMatthew Dillon exit(1); 83534ebae70SMatthew Dillon } 836*17dd83bcSMatthew Dillon if (mrec->head.rec_size != sizeof(mrec->pfs)) { 837243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: unexpected payload " 838243ca327SMatthew Dillon "size\n"); 83934ebae70SMatthew Dillon exit(1); 84034ebae70SMatthew Dillon } 841*17dd83bcSMatthew Dillon if (mrec->pfs.version != pfs.version) { 842243ca327SMatthew Dillon fprintf(stderr, "validate_mrec_header: Version mismatch\n"); 84334ebae70SMatthew Dillon exit(1); 84434ebae70SMatthew Dillon } 84534ebae70SMatthew Dillon 84634ebae70SMatthew Dillon /* 84734ebae70SMatthew Dillon * Whew. Ok, is the read PFS info compatible with the target? 84834ebae70SMatthew Dillon */ 849*17dd83bcSMatthew Dillon if (bcmp(&mrec->pfs.pfsd.shared_uuid, &pfsd.shared_uuid, 850*17dd83bcSMatthew Dillon sizeof(pfsd.shared_uuid)) != 0) { 851*17dd83bcSMatthew Dillon fprintf(stderr, 852*17dd83bcSMatthew Dillon "mirror-write: source and target have " 853*17dd83bcSMatthew Dillon "different shared_uuid's!\n"); 85434ebae70SMatthew Dillon exit(1); 85534ebae70SMatthew Dillon } 856d4e5b69bSMatthew Dillon if (is_target && 857d4e5b69bSMatthew Dillon (pfsd.mirror_flags & HAMMER_PFSD_SLAVE) == 0) { 85834ebae70SMatthew Dillon fprintf(stderr, "mirror-write: target must be in slave mode\n"); 85934ebae70SMatthew Dillon exit(1); 86034ebae70SMatthew Dillon } 861d4e5b69bSMatthew Dillon if (tid_begp) 862*17dd83bcSMatthew Dillon *tid_begp = mrec->pfs.pfsd.sync_beg_tid; 863d4e5b69bSMatthew Dillon if (tid_endp) 864*17dd83bcSMatthew Dillon *tid_endp = mrec->pfs.pfsd.sync_end_tid; 865243ca327SMatthew Dillon free(mrec); 86634ebae70SMatthew Dillon } 86734ebae70SMatthew Dillon 86834ebae70SMatthew Dillon static void 869d4e5b69bSMatthew Dillon update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id) 87034ebae70SMatthew Dillon { 871243ca327SMatthew Dillon struct hammer_ioc_pseudofs_rw pfs; 872243ca327SMatthew Dillon struct hammer_pseudofs_data pfsd; 87334ebae70SMatthew Dillon 874243ca327SMatthew Dillon bzero(&pfs, sizeof(pfs)); 875243ca327SMatthew Dillon bzero(&pfsd, sizeof(pfsd)); 876d4e5b69bSMatthew Dillon pfs.pfs_id = pfs_id; 877243ca327SMatthew Dillon pfs.ondisk = &pfsd; 878243ca327SMatthew Dillon pfs.bytes = sizeof(pfsd); 879243ca327SMatthew Dillon if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 880243ca327SMatthew Dillon perror("update_pfs_snapshot (read)"); 881243ca327SMatthew Dillon exit(1); 88234ebae70SMatthew Dillon } 883ddc8e722SMatthew Dillon pfsd.sync_end_tid = snapshot_tid; 884243ca327SMatthew Dillon if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) != 0) { 885243ca327SMatthew Dillon perror("update_pfs_snapshot (rewrite)"); 886243ca327SMatthew Dillon exit(1); 88734ebae70SMatthew Dillon } 888243ca327SMatthew Dillon } 889243ca327SMatthew Dillon 89034ebae70SMatthew Dillon 891a7fbbf91SMatthew Dillon static void 892a7fbbf91SMatthew Dillon mirror_usage(int code) 893a7fbbf91SMatthew Dillon { 894a7fbbf91SMatthew Dillon fprintf(stderr, 895a7fbbf91SMatthew Dillon "hammer mirror-read <filesystem>\n" 896a7fbbf91SMatthew Dillon "hammer mirror-write <filesystem>\n" 897243ca327SMatthew Dillon "hammer mirror-dump\n" 898a7fbbf91SMatthew Dillon "hammer mirror-copy [[user@]host:]fs [[user@]host:]fs\n" 899a7fbbf91SMatthew Dillon ); 900a7fbbf91SMatthew Dillon exit(code); 901a7fbbf91SMatthew Dillon } 902