Ejemplo n.º 1
0
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);
}
Ejemplo n.º 2
0
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);
	}
}
Ejemplo n.º 3
0
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);
}
Ejemplo n.º 4
0
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);
}
Ejemplo n.º 5
0
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);
		}
	}
}
Ejemplo n.º 6
0
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));
	}
}
Ejemplo n.º 7
0
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));
	}
}
Ejemplo n.º 8
0
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);
}
Ejemplo n.º 9
0
/*
 * 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);
}
Ejemplo n.º 10
0
/*
 * 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;
}