void hammer_cmd_pseudofs_downgrade(char **av, int ac) { struct hammer_ioc_pseudofs_rw pfs; int fd; if (ac == 0) pseudofs_usage(1); fd = getpfs(&pfs, av[0]); if (pfs.pfs_id == 0) { fprintf(stderr, "You cannot downgrade PFS#0\n"); exit(1); } else if (pfs.ondisk->mirror_flags & HAMMER_PFSD_SLAVE) { printf("It is already a slave\n"); exit(1); } if (ioctl(fd, HAMMERIOC_DGD_PSEUDOFS, &pfs) == 0) { printf("pfs-downgrade of PFS#%d (%s) succeeded\n", pfs.pfs_id, pfs.ondisk->label); } else { fprintf(stderr, "pfs-downgrade of PFS#%d (%s) failed: %s\n", pfs.pfs_id, pfs.ondisk->label, strerror(errno)); } relpfs(fd, &pfs); }
void hammer_cmd_pseudofs_status(char **av, int ac) { struct hammer_ioc_pseudofs_rw pfs; int i; int fd; if (ac == 0) pseudofs_usage(1); for (i = 0; i < ac; ++i) { printf("%s\t", av[i]); fd = getpfs(&pfs, av[i]); if (fd < 0 || ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) < 0) { printf("Not a HAMMER root\n"); } else { printf("PFS #%d {\n", pfs.pfs_id); dump_pfsd(pfs.ondisk, fd); printf("}\n"); } if (fd >= 0) close(fd); if (pfs.ondisk) free(pfs.ondisk); } }
void hammer_cmd_pseudofs_upgrade(char **av, int ac) { struct hammer_ioc_pseudofs_rw pfs; int fd; if (ac == 0) pseudofs_usage(1); fd = getpfs(&pfs, av[0]); if (pfs.pfs_id == HAMMER_ROOT_PFSID) { fprintf(stderr, "You cannot upgrade PFS#0" " (It should already be a master)\n"); exit(1); } else if (hammer_is_pfs_master(pfs.ondisk)) { printf("It is already a master\n"); exit(1); } if (ioctl(fd, HAMMERIOC_UPG_PSEUDOFS, &pfs) == 0) { printf("pfs-upgrade of PFS#%d (%s) succeeded\n", pfs.pfs_id, pfs.ondisk->label); } else { fprintf(stderr, "pfs-upgrade of PFS#%d (%s) failed: %s\n", pfs.pfs_id, pfs.ondisk->label, strerror(errno)); } relpfs(fd, &pfs); }
static void print_pfs_status(char *path) { struct hammer_ioc_pseudofs_rw pfs; int fd; fd = getpfs(&pfs, path); printf("%s\t", path); if (fd < 0 || ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) < 0) { printf("Invalid PFS path %s\n", path); } else { printf("PFS#%d {\n", pfs.pfs_id); dump_pfsd(pfs.ondisk, fd); printf("}\n"); } if (fd >= 0) close(fd); if (pfs.ondisk) free(pfs.ondisk); relpfs(fd, &pfs); }
void hammer_cmd_pseudofs_update(char **av, int ac) { struct hammer_ioc_pseudofs_rw pfs; int fd; if (ac == 0) pseudofs_usage(1); bzero(&pfs, sizeof(pfs)); fd = getpfs(&pfs, av[0]); printf("%s\n", av[0]); fflush(stdout); if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) == 0) { parse_pfsd_options(av + 1, ac - 1, pfs.ondisk); if ((pfs.ondisk->mirror_flags & HAMMER_PFSD_SLAVE) && pfs.pfs_id == 0) { printf("The real mount point cannot be made a PFS " "slave, only PFS sub-directories can be made " "slaves\n"); exit(1); } pfs.bytes = sizeof(*pfs.ondisk); if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) == 0) { if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) == 0) { dump_pfsd(pfs.ondisk, fd); } else { printf("Unable to retrieve pfs configuration " "after successful update: %s\n", strerror(errno)); exit(1); } } else { printf("Unable to adjust pfs configuration: %s\n", strerror(errno)); exit(1); } } }
void hammer_cmd_pseudofs_downgrade(char **av, int ac) { struct hammer_ioc_pseudofs_rw pfs; int fd; if (ac == 0) pseudofs_usage(1); bzero(&pfs, sizeof(pfs)); fd = getpfs(&pfs, av[0]); if (pfs.pfs_id == 0) { fprintf(stderr, "You cannot downgrade PFS#0\n"); exit(1); } if (ioctl(fd, HAMMERIOC_DGD_PSEUDOFS, &pfs) == 0) { printf("pfs-downgrade of PFS#%d (%s) succeeded\n", pfs.pfs_id, pfs.ondisk->label); } else { fprintf(stderr, "pfs-upgrade of PFS#%d (%s) failed: %s\n", pfs.pfs_id, pfs.ondisk->label, strerror(errno)); } }
void hammer_cmd_pseudofs_destroy(char **av, int ac) { struct hammer_ioc_pseudofs_rw pfs; struct stat st; int fd; int i; if (ac == 0) pseudofs_usage(1); bzero(&pfs, sizeof(pfs)); fd = getpfs(&pfs, av[0]); if (pfs.pfs_id == 0) { fprintf(stderr, "You cannot destroy PFS#0\n"); exit(1); } printf("You have requested that PFS#%d (%s) be destroyed\n", pfs.pfs_id, pfs.ondisk->label); printf("This will irrevocably destroy all data on this PFS!!!!!\n"); printf("Do you really want to do this? "); fflush(stdout); if (getyn() == 0) { fprintf(stderr, "No action taken on PFS#%d\n", pfs.pfs_id); exit(1); } if ((pfs.ondisk->mirror_flags & HAMMER_PFSD_SLAVE) == 0) { printf("This PFS is currently setup as a MASTER!\n"); printf("Are you absolutely sure you want to destroy it? "); fflush(stdout); if (getyn() == 0) { fprintf(stderr, "No action taken on PFS#%d\n", pfs.pfs_id); exit(1); } } printf("Destroying PFS #%d (%s) in ", pfs.pfs_id, pfs.ondisk->label); for (i = 5; i; --i) { printf(" %d", i); fflush(stdout); sleep(1); } printf(".. starting destruction pass\n"); fflush(stdout); /* * Set the sync_beg_tid and sync_end_tid's to 1, once we start the * RMR the PFS is basically destroyed even if someone ^C's it. */ pfs.ondisk->mirror_flags |= HAMMER_PFSD_SLAVE; pfs.ondisk->reserved01 = -1; pfs.ondisk->sync_beg_tid = 1; pfs.ondisk->sync_end_tid = 1; if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) < 0) { fprintf(stderr, "Unable to update the PFS configuration: %s\n", strerror(errno)); exit(1); } /* * Ok, do it. Remove the softlink on success. */ if (ioctl(fd, HAMMERIOC_RMR_PSEUDOFS, &pfs) == 0) { printf("pfs-destroy of PFS#%d succeeded!\n", pfs.pfs_id); if (lstat(av[0], &st) == 0 && S_ISLNK(st.st_mode)) { if (remove(av[0]) < 0) { fprintf(stderr, "Unable to remove softlink: %s " "(but the PFS has been destroyed)\n", av[0]); /* exit status 0 anyway */ } } } else { printf("pfs-destroy of PFS#%d failed: %s\n", pfs.pfs_id, strerror(errno)); } }
void hammer_cmd_pseudofs_destroy(char **av, int ac) { struct hammer_ioc_pseudofs_rw pfs; char *linkpath; int fd; int i; if (ac == 0) pseudofs_usage(1); fd = getpfs(&pfs, av[0]); if (pfs.pfs_id == HAMMER_ROOT_PFSID) { fprintf(stderr, "You cannot destroy PFS#0\n"); exit(1); } printf("You have requested that PFS#%d (%s) be destroyed\n", pfs.pfs_id, pfs.ondisk->label); printf("This will irrevocably destroy all data on this PFS!!!!!\n"); printf("Do you really want to do this? [y/n] "); fflush(stdout); if (getyn() == 0) { fprintf(stderr, "No action taken on PFS#%d\n", pfs.pfs_id); exit(1); } if (hammer_is_pfs_master(pfs.ondisk)) { printf("This PFS is currently setup as a MASTER!\n"); printf("Are you absolutely sure you want to destroy it? [y/n] "); fflush(stdout); if (getyn() == 0) { fprintf(stderr, "No action taken on PFS#%d\n", pfs.pfs_id); exit(1); } } printf("Destroying PFS#%d (%s)", pfs.pfs_id, pfs.ondisk->label); if (DebugOpt) { printf("\n"); } else { printf(" in"); for (i = 5; i; --i) { printf(" %d", i); fflush(stdout); sleep(1); } printf(".. starting destruction pass\n"); } /* * Remove the softlink on success. */ if (ioctl(fd, HAMMERIOC_RMR_PSEUDOFS, &pfs) == 0) { printf("pfs-destroy of PFS#%d succeeded!\n", pfs.pfs_id); linkpath = getlink(av[0]); if (linkpath) { if (remove(linkpath) < 0) { fprintf(stderr, "Unable to remove softlink %s: %s\n", linkpath, strerror(errno)); /* exit status 0 anyway */ } free(linkpath); } } else { printf("pfs-destroy of PFS#%d failed: %s\n", pfs.pfs_id, strerror(errno)); } relpfs(fd, &pfs); }
/* * Generate a mirroring data stream from the specific source over the * entire key range, but restricted to the specified transaction range. * * The HAMMER VFS does most of the work, we add a few new mrecord * types to negotiate the TID ranges and verify that the entire * stream made it to the destination. * * streaming will be 0 for mirror-read, 1 for mirror-stream. The code will * set up a fake value of -1 when running the histogram for mirror-read. */ void hammer_cmd_mirror_read(char **av, int ac, int streaming) { struct hammer_ioc_mirror_rw mirror; struct hammer_ioc_pseudofs_rw pfs; union hammer_ioc_mrecord_any mrec_tmp; struct hammer_ioc_mrecord_head pickup; hammer_ioc_mrecord_any_t mrec; hammer_tid_t sync_tid; histogram_t histogram_ary; const char *filesystem; char *buf = malloc(SERIALBUF_SIZE); int interrupted = 0; int error; int fd; int n; int didwork; int histogram; int histindex; int histmax; int repeat = 0; int sameline; int64_t total_bytes; time_t base_t = time(NULL); struct timeval bwtv; uint64_t bwcount; uint64_t estbytes; if (ac == 0 || ac > 2) mirror_usage(1); filesystem = av[0]; hammer_check_restrict(filesystem); pickup.signature = 0; pickup.type = 0; histogram = 0; histindex = 0; histmax = 0; histogram_ary = NULL; sameline = 0; again: bzero(&mirror, sizeof(mirror)); hammer_key_beg_init(&mirror.key_beg); hammer_key_end_init(&mirror.key_end); fd = getpfs(&pfs, filesystem); if (streaming >= 0) score_printf(LINE1, "Running"); if (streaming >= 0 && VerboseOpt && VerboseOpt < 2) { fprintf(stderr, "%cRunning \b\b", (sameline ? '\r' : '\n')); fflush(stderr); sameline = 1; } sameline = 1; total_bytes = 0; gettimeofday(&bwtv, NULL); bwcount = 0; /* * Send initial header for the purpose of determining the * shared-uuid. */ generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp); write_mrecord(1, HAMMER_MREC_TYPE_PFSD, &mrec_tmp, sizeof(mrec_tmp.pfs)); /* * In 2-way mode the target will send us a PFS info packet * first. Use the target's current snapshot TID as our default * begin TID. */ if (TwoWayPipeOpt) { mirror.tid_beg = 0; n = validate_mrec_header(fd, 0, 0, pfs.pfs_id, &pickup, NULL, &mirror.tid_beg); if (n < 0) { /* got TERM record */ relpfs(fd, &pfs); free(buf); free(histogram_ary); return; } ++mirror.tid_beg; } else if (streaming && histogram) { mirror.tid_beg = histogram_ary[histindex].tid + 1; } else { mirror.tid_beg = 0; } /* * Write out the PFS header, tid_beg will be updated if our PFS * has a larger begin sync. tid_end is set to the latest source * TID whos flush cycle has completed. */ generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp); if (mirror.tid_beg < mrec_tmp.pfs.pfsd.sync_beg_tid) mirror.tid_beg = mrec_tmp.pfs.pfsd.sync_beg_tid; mirror.tid_end = mrec_tmp.pfs.pfsd.sync_end_tid; mirror.ubuf = buf; mirror.size = SERIALBUF_SIZE; mirror.pfs_id = pfs.pfs_id; mirror.shared_uuid = pfs.ondisk->shared_uuid; /* * XXX If the histogram is exhausted and the TID delta is large * the stream might have been offline for a while and is * now picking it up again. Do another histogram. */ #if 0 if (streaming && histogram && histindex == histend) { if (mirror.tid_end - mirror.tid_beg > BULK_MINIMUM) histogram = 0; } #endif /* * Initial bulk startup control, try to do some incremental * mirroring in order to allow the stream to be killed and * restarted without having to start over. */ if (histogram == 0 && BulkOpt == 0) { if (VerboseOpt && repeat == 0) { fprintf(stderr, "\n"); sameline = 0; } histmax = generate_histogram(fd, filesystem, &histogram_ary, &mirror, &repeat); histindex = 0; histogram = 1; /* * Just stream the histogram, then stop */ if (streaming == 0) streaming = -1; } if (streaming && histogram) { ++histindex; mirror.tid_end = histogram_ary[histindex].tid; estbytes = histogram_ary[histindex-1].bytes; mrec_tmp.pfs.pfsd.sync_end_tid = mirror.tid_end; } else { estbytes = 0; } write_mrecord(1, HAMMER_MREC_TYPE_PFSD, &mrec_tmp, sizeof(mrec_tmp.pfs)); /* * A cycle file overrides the beginning TID only if we are * not operating in two-way or histogram mode. */ if (TwoWayPipeOpt == 0 && histogram == 0) { hammer_get_cycle(&mirror.key_beg, &mirror.tid_beg); } /* * An additional argument overrides the beginning TID regardless * of what mode we are in. This is not recommending if operating * in two-way mode. */ if (ac == 2) mirror.tid_beg = strtoull(av[1], NULL, 0); if (streaming == 0 || VerboseOpt >= 2) { fprintf(stderr, "Mirror-read: Mirror %016jx to %016jx", (uintmax_t)mirror.tid_beg, (uintmax_t)mirror.tid_end); if (histogram) fprintf(stderr, " (bulk= %ju)", (uintmax_t)estbytes); fprintf(stderr, "\n"); fflush(stderr); } if (mirror.key_beg.obj_id != (int64_t)HAMMER_MIN_OBJID) { fprintf(stderr, "Mirror-read: Resuming at object %016jx\n", (uintmax_t)mirror.key_beg.obj_id); } /* * Nothing to do if begin equals end. */ if (mirror.tid_beg >= mirror.tid_end) { if (streaming == 0 || VerboseOpt >= 2) fprintf(stderr, "Mirror-read: No work to do\n"); sleep(DelayOpt); didwork = 0; histogram = 0; goto done; } didwork = 1; /* * Write out bulk records */ mirror.ubuf = buf; mirror.size = SERIALBUF_SIZE; do { mirror.count = 0; mirror.pfs_id = pfs.pfs_id; mirror.shared_uuid = pfs.ondisk->shared_uuid; if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) { score_printf(LINE3, "Mirror-read %s failed: %s", filesystem, strerror(errno)); fprintf(stderr, "Mirror-read %s failed: %s\n", filesystem, strerror(errno)); exit(1); } if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) { score_printf(LINE3, "Mirror-read %s fatal error %d", filesystem, mirror.head.error); fprintf(stderr, "Mirror-read %s fatal error %d\n", filesystem, mirror.head.error); exit(1); } if (mirror.count) { if (BandwidthOpt) { n = writebw(1, mirror.ubuf, mirror.count, &bwcount, &bwtv); } else { n = write(1, mirror.ubuf, mirror.count); } if (n != mirror.count) { score_printf(LINE3, "Mirror-read %s failed: " "short write", filesystem); fprintf(stderr, "Mirror-read %s failed: " "short write\n", filesystem); exit(1); } } total_bytes += mirror.count; if (streaming && VerboseOpt) { fprintf(stderr, "\rscan obj=%016jx tids=%016jx:%016jx %11jd", (uintmax_t)mirror.key_cur.obj_id, (uintmax_t)mirror.tid_beg, (uintmax_t)mirror.tid_end, (intmax_t)total_bytes); fflush(stderr); sameline = 0; } else if (streaming) { score_printf(LINE2, "obj=%016jx tids=%016jx:%016jx %11jd", (uintmax_t)mirror.key_cur.obj_id, (uintmax_t)mirror.tid_beg, (uintmax_t)mirror.tid_end, (intmax_t)total_bytes); } mirror.key_beg = mirror.key_cur; /* * Deal with time limit option */ if (TimeoutOpt && (unsigned)(time(NULL) - base_t) > (unsigned)TimeoutOpt) { score_printf(LINE3, "Mirror-read %s interrupted by timer at" " %016jx", filesystem, (uintmax_t)mirror.key_cur.obj_id); fprintf(stderr, "Mirror-read %s interrupted by timer at" " %016jx\n", filesystem, (uintmax_t)mirror.key_cur.obj_id); interrupted = 1; break; } } while (mirror.count != 0); done: if (streaming && VerboseOpt && sameline == 0) { fprintf(stderr, "\n"); fflush(stderr); sameline = 1; } /* * Write out the termination sync record - only if not interrupted */ if (interrupted == 0) { if (didwork) { write_mrecord(1, HAMMER_MREC_TYPE_SYNC, &mrec_tmp, sizeof(mrec_tmp.sync)); } else { write_mrecord(1, HAMMER_MREC_TYPE_IDLE, &mrec_tmp, sizeof(mrec_tmp.sync)); } } /* * If the -2 option was given (automatic when doing mirror-copy), * a two-way pipe is assumed and we expect a response mrec from * the target. */ if (TwoWayPipeOpt) { mrec = read_mrecord(0, &error, &pickup); if (mrec == NULL || mrec->head.type != HAMMER_MREC_TYPE_UPDATE || mrec->head.rec_size != sizeof(mrec->update)) { fprintf(stderr, "mirror_read: Did not get final " "acknowledgement packet from target\n"); exit(1); } if (interrupted) { if (CyclePath) { hammer_set_cycle(&mirror.key_cur, mirror.tid_beg); fprintf(stderr, "Cyclefile %s updated for " "continuation\n", CyclePath); } } else { sync_tid = mrec->update.tid; if (CyclePath) { hammer_key_beg_init(&mirror.key_beg); hammer_set_cycle(&mirror.key_beg, sync_tid); fprintf(stderr, "Cyclefile %s updated to 0x%016jx\n", CyclePath, (uintmax_t)sync_tid); } } free(mrec); } else if (CyclePath) { /* NOTE! mirror.tid_beg cannot be updated */ fprintf(stderr, "Warning: cycle file (-c option) cannot be " "fully updated unless you use mirror-copy\n"); hammer_set_cycle(&mirror.key_beg, mirror.tid_beg); } if (streaming && interrupted == 0) { time_t t1 = time(NULL); time_t t2; /* * Try to break down large bulk transfers into smaller ones * so it can sync the transaction id on the slave. This * way if we get interrupted a restart doesn't have to * start from scratch. */ if (streaming && histogram) { if (histindex != histmax) { if (VerboseOpt && VerboseOpt < 2 && streaming >= 0) { fprintf(stderr, " (bulk incremental)"); } relpfs(fd, &pfs); goto again; } } if (VerboseOpt && streaming >= 0) { fprintf(stderr, " W"); fflush(stderr); } else if (streaming >= 0) { score_printf(LINE1, "Waiting"); } pfs.ondisk->sync_end_tid = mirror.tid_end; if (streaming < 0) { /* * Fake streaming mode when using a histogram to * break up a mirror-read, do not wait on source. */ streaming = 0; } else if (ioctl(fd, HAMMERIOC_WAI_PSEUDOFS, &pfs) < 0) { score_printf(LINE3, "Mirror-read %s: cannot stream: %s\n", filesystem, strerror(errno)); fprintf(stderr, "Mirror-read %s: cannot stream: %s\n", filesystem, strerror(errno)); } else { t2 = time(NULL) - t1; if (t2 >= 0 && t2 < DelayOpt) { if (VerboseOpt) { fprintf(stderr, "\bD"); fflush(stderr); } sleep(DelayOpt - t2); } if (VerboseOpt) { fprintf(stderr, "\b "); fflush(stderr); } relpfs(fd, &pfs); goto again; } } write_mrecord(1, HAMMER_MREC_TYPE_TERM, &mrec_tmp, sizeof(mrec_tmp.sync)); relpfs(fd, &pfs); free(buf); free(histogram_ary); fprintf(stderr, "Mirror-read %s succeeded\n", filesystem); }
/* * Pipe the mirroring data stream on stdin to the HAMMER VFS, adding * some additional packet types to negotiate TID ranges and to verify * completion. The HAMMER VFS does most of the work. * * It is important to note that the mirror.key_{beg,end} range must * match the ranged used by the original. For now both sides use * range the entire key space. * * It is even more important that the records in the stream conform * to the TID range also supplied in the stream. The HAMMER VFS will * use the REC, PASS, and SKIP record types to track the portions of * the B-Tree being scanned in order to be able to proactively delete * records on the target within those active areas that are not mentioned * by the source. * * The mirror.key_cur field is used by the VFS to do this tracking. It * must be initialized to key_beg but then is persistently updated by * the HAMMER VFS on each successive ioctl() call. If you blow up this * field you will blow up the mirror target, possibly to the point of * deleting everything. As a safety measure the HAMMER VFS simply marks * the records that the source has destroyed as deleted on the target, * and normal pruning operations will deal with their final disposition * at some later time. */ void hammer_cmd_mirror_write(char **av, int ac) { struct hammer_ioc_mirror_rw mirror; const char *filesystem; char *buf = malloc(SERIALBUF_SIZE); struct hammer_ioc_pseudofs_rw pfs; struct hammer_ioc_mrecord_head pickup; struct hammer_ioc_synctid synctid; union hammer_ioc_mrecord_any mrec_tmp; hammer_ioc_mrecord_any_t mrec; struct stat st; int error; int fd; int n; if (ac != 1) mirror_usage(1); filesystem = av[0]; hammer_check_restrict(filesystem); pickup.signature = 0; pickup.type = 0; again: bzero(&mirror, sizeof(mirror)); hammer_key_beg_init(&mirror.key_beg); hammer_key_end_init(&mirror.key_end); mirror.key_end = mirror.key_beg; /* * Read initial packet */ mrec = read_mrecord(0, &error, &pickup); if (mrec == NULL) { if (error == 0) fprintf(stderr, "validate_mrec_header: short read\n"); exit(1); } /* * Validate packet */ if (mrec->head.type == HAMMER_MREC_TYPE_TERM) { free(buf); return; } if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) { fprintf(stderr, "validate_mrec_header: did not get expected " "PFSD record type\n"); exit(1); } if (mrec->head.rec_size != sizeof(mrec->pfs)) { fprintf(stderr, "validate_mrec_header: unexpected payload " "size\n"); exit(1); } /* * Create slave PFS if it doesn't yet exist */ if (lstat(filesystem, &st) != 0) { create_pfs(filesystem, &mrec->pfs.pfsd.shared_uuid); } free(mrec); mrec = NULL; fd = getpfs(&pfs, filesystem); /* * In two-way mode the target writes out a PFS packet first. * The source uses our tid_end as its tid_beg by default, * picking up where it left off. */ mirror.tid_beg = 0; if (TwoWayPipeOpt) { generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp); if (mirror.tid_beg < mrec_tmp.pfs.pfsd.sync_beg_tid) mirror.tid_beg = mrec_tmp.pfs.pfsd.sync_beg_tid; mirror.tid_end = mrec_tmp.pfs.pfsd.sync_end_tid; write_mrecord(1, HAMMER_MREC_TYPE_PFSD, &mrec_tmp, sizeof(mrec_tmp.pfs)); } /* * Read and process the PFS header. The source informs us of * the TID range the stream represents. */ n = validate_mrec_header(fd, 0, 1, pfs.pfs_id, &pickup, &mirror.tid_beg, &mirror.tid_end); if (n < 0) { /* got TERM record */ relpfs(fd, &pfs); free(buf); return; } mirror.ubuf = buf; mirror.size = SERIALBUF_SIZE; /* * Read and process bulk records (REC, PASS, and SKIP types). * * On your life, do NOT mess with mirror.key_cur or your mirror * target may become history. */ for (;;) { mirror.count = 0; mirror.pfs_id = pfs.pfs_id; mirror.shared_uuid = pfs.ondisk->shared_uuid; mirror.size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup); if (mirror.size <= 0) break; if (ioctl(fd, HAMMERIOC_MIRROR_WRITE, &mirror) < 0) { fprintf(stderr, "Mirror-write %s failed: %s\n", filesystem, strerror(errno)); exit(1); } if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) { fprintf(stderr, "Mirror-write %s fatal error %d\n", filesystem, mirror.head.error); exit(1); } #if 0 if (mirror.head.flags & HAMMER_IOC_HEAD_INTR) { fprintf(stderr, "Mirror-write %s interrupted by timer at" " %016llx\n", filesystem, mirror.key_cur.obj_id); exit(0); } #endif } /* * Read and process the termination sync record. */ mrec = read_mrecord(0, &error, &pickup); if (mrec && mrec->head.type == HAMMER_MREC_TYPE_TERM) { fprintf(stderr, "Mirror-write: received termination request\n"); relpfs(fd, &pfs); free(mrec); free(buf); return; } if (mrec == NULL || (mrec->head.type != HAMMER_MREC_TYPE_SYNC && mrec->head.type != HAMMER_MREC_TYPE_IDLE) || mrec->head.rec_size != sizeof(mrec->sync)) { fprintf(stderr, "Mirror-write %s: Did not get termination " "sync record, or rec_size is wrong rt=%d\n", filesystem, (mrec ? (int)mrec->head.type : -1)); exit(1); } /* * Update the PFS info on the target so the user has visibility * into the new snapshot, and sync the target filesystem. */ if (mrec->head.type == HAMMER_MREC_TYPE_SYNC) { update_pfs_snapshot(fd, mirror.tid_end, pfs.pfs_id); bzero(&synctid, sizeof(synctid)); synctid.op = HAMMER_SYNCTID_SYNC2; ioctl(fd, HAMMERIOC_SYNCTID, &synctid); if (VerboseOpt >= 2) { fprintf(stderr, "Mirror-write %s: succeeded\n", filesystem); } } free(mrec); mrec = NULL; /* * Report back to the originator. */ if (TwoWayPipeOpt) { mrec_tmp.update.tid = mirror.tid_end; write_mrecord(1, HAMMER_MREC_TYPE_UPDATE, &mrec_tmp, sizeof(mrec_tmp.update)); } else { printf("Source can update synctid to 0x%016jx\n", (uintmax_t)mirror.tid_end); } relpfs(fd, &pfs); goto again; }