blob: 13773059865d6d6a2b99db4f6daabda38e6ece43 [file] [log] [blame]
/* ============================================================
* Copyright (c) 2014 Actifio Inc. All Rights Reserved
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "bitmap.h"
#ifndef ZFS_MAXNAMELEN
#define ZFS_MAXNAMELEN (MAXNAMELEN - 1)
#endif
#define GRAINSIZE ((unsigned)(64*1024))
int verbosity = 0;
int debug = 0;
unsigned int grainsize = GRAINSIZE;
int usage(void)
{
printf("Bitmap generation usage:\n");
printf(" zfstool [options] [bitmap|zst-bitmap] clone1-name clone2-name /path/to/bitmap/file\n");
printf("\toptions include:\n"
"\t -v - verbosity (repeated one or more times, e.g. -v -v -v)\n"
"\t -g size - grain size in bytes, default %d\n"
"\t -z - generate a separate bitmap of writes, stored in /path/to/bitmap/file,\n"
"\t\t and a bitmap of holes, stored in /path/to/bitmap/file.holes,\n"
"\t -c /path/to/clone1/holes - option is valid only if -z is provided,\n"
"\t\t provides path to the hole bitmap file for clone1\n",
grainsize);
printf("\tcommands include:\n"
"\t bitmap - use ZFS send stream to produce bitmap\n"
"\t zst-bitmap - same as bitmap, included for backward compatibility\n");
printf("Bitmap verification test usage:\n");
printf(" zfstool [options] bitmap-verify clone-name clone-name {byte_offset}\n");
printf("\toptions include:\n"
"\t -v - verbosity (repeated one or more times, e.g. -v -v -v)\n"
"\t -g size - grain size in bytes, default %d\n"
"\t -z - generate separate bitmaps for writes and frees\n"
"\t -c /path/to/clone1/holes - option is valid only if -z is provided,\n"
"\t\t provides path to the hole bitmap file for clone1\n"
"\t if the byte_offset is specified, check the corresponding grain,\n"
"\t otherwise, the whole bitmap is verified\n",
grainsize);
printf("Bitmap display usage:\n");
printf(" zfstool bitmap-show bitmap-file {byte_offset}\n"
"\t if the byte_offset is specified, show the corresponding grain,\n"
"\t otherwise, the whole bitmap is displayed\n");
return (-1);
}
/* ZFS send style bitmap calculation */
#include <sys/fs/zfs.h>
#include <libzfs.h>
#include <sys/zfs_ioctl.h>
#include "zst.h"
#define ZVOL_OBJECT (1)
#define ZVOL_OBJLEN_MARKER (~0ULL)
typedef struct {
ram_bitmap_pair_t *bitmap_pair;
uint64_t parent_volsize;
uint64_t child_volsize;
} zst_callback_arg_t;
/*
* Callbacks to be registered with ZST for getting the block level volume diffs
*/
static int
write_callback(int type, void *arg1, void *arg2)
{
struct drr_write *drrw = (struct drr_write *)arg1;
zst_callback_arg_t *arg = (zst_callback_arg_t *)arg2;
assert(type == DRR_WRITE);
/* skip non-zvol object */
if (drrw->drr_object != ZVOL_OBJECT)
return (0);
if (drrw->drr_offset + drrw->drr_logical_size > arg->child_volsize) {
fprintf(stderr, "Write [0x%" PRIx64 ":0x%" PRIx64 ") outside zvol "
"byte range [0x0:0x%" PRIx64 ")\n",
drrw->drr_offset, drrw->drr_logical_size, arg->child_volsize);
return (EINVAL);
}
if (verbosity > 1)
fprintf(stderr, "Write record found [0x%" PRIx64 ":0x%" PRIx64 ")\n",
drrw->drr_offset, drrw->drr_offset + drrw->drr_logical_size);
/* make a call to change the bitmap */
bitmap_addbit(arg->bitmap_pair->diff_bitmap, drrw->drr_offset, drrw->drr_logical_size);
return (0);
}
static int
write_embedded_callback(int type, void *arg1, void *arg2)
{
struct drr_write_embedded *drrwe = (struct drr_write_embedded *)arg1;
zst_callback_arg_t *arg = (zst_callback_arg_t *)arg2;
assert(type == DRR_WRITE_EMBEDDED);
if (drrwe->drr_offset + drrwe->drr_length > arg->child_volsize) {
fprintf(stderr, "Write [%" PRIx64 ":%" PRIx64 ") outside zvol "
"byte range [0:%" PRIx64 ")\n",
drrwe->drr_offset, drrwe->drr_length, arg->child_volsize);
return (EINVAL);
}
/* skip non-zvol object */
if (drrwe->drr_object != ZVOL_OBJECT)
return (0);
if (verbosity)
fprintf(stderr, "Write record found [%" PRIx64 ":%" PRIx64 ")\n",
drrwe->drr_offset, drrwe->drr_offset + drrwe->drr_length);
/* make a call to change the bitmap */
bitmap_addbit(arg->bitmap_pair->diff_bitmap, drrwe->drr_offset, drrwe->drr_length);
return (0);
}
static int
free_callback(int type, void *arg1, void *arg2)
{
struct drr_free *drrf = (struct drr_free *)arg1;
zst_callback_arg_t *arg = (zst_callback_arg_t *)arg2;
assert(type == DRR_FREE);
/* skip non-zvol object */
if (drrf->drr_object != ZVOL_OBJECT)
return (0);
if (drrf->drr_length == ZVOL_OBJLEN_MARKER) {
if (verbosity > 1)
fprintf(stderr, "Truncate record found; offset 0x%" PRIx64 "\n",
drrf->drr_offset);
if (drrf->drr_offset > arg->child_volsize) {
fprintf(stderr, "Offset 0x%" PRIx64 " in trancate record is "
"greater than child_volsize 0x%" PRIx64 "\n",
drrf->drr_offset, arg->child_volsize);
return (EINVAL);
}
/*
* avoid writing zeros in place of a whole at the end of the child
* volume; update the size to the last offset used
*/
arg->child_volsize = drrf->drr_offset;
return (0);
}
if (verbosity > 1)
fprintf(stderr, "Free record found [0x%" PRIx64 ":0x%" PRIx64 ")\n",
drrf->drr_offset, drrf->drr_offset + drrf->drr_length);
/*
* it is necessary to keep track of new holes that can be ligitimately
* created with overwrites with zeros and block discards
*/
if (drrf->drr_offset + drrf->drr_length > arg->child_volsize) {
if (verbosity > 1)
fprintf(stderr, "\tThis free record goes outside the larger zvol "
"byte range [0x0:0x%"PRIx64"), perhaps due to previously "
"encountered truncate\n", arg->child_volsize);
if (drrf->drr_offset >= arg->child_volsize) {
if (verbosity > 1)
fprintf(stderr, "\tThis free record if entirely outside "
"zvol byte range [0x0:0x%"PRIx64"), perhaps due to "
"previously encountered truncate\n",
arg->child_volsize);
return (0);
}
/*
* adjust the lenght such that the change is within the zvol byte range
*/
drrf->drr_length = arg->child_volsize - drrf->drr_offset;
if (verbosity > 1)
fprintf(stderr, "Adjusted record to [0x%" PRIx64 ":0x%" PRIx64 ")\n",
drrf->drr_offset, drrf->drr_offset + drrf->drr_length);
}
/*
* Make a call to change bitmaps.
* If free_bitmap is present, generate separate bitmaps for writes and frees
* and therefore, update the free_bitmap; otherwise, generate a consolidated
* bitmap by writing free bits into the diff_bitmap.
*/
if (arg->bitmap_pair->free_bitmap) {
if (verbosity > 1)
fprintf(stderr, "Generating separate bitmap diffs for free region\n");
bitmap_addbit(arg->bitmap_pair->free_bitmap, drrf->drr_offset, drrf->drr_length);
} else {
if (verbosity > 1)
fprintf(stderr, "Generating consolidated bitmap diffs for free region\n");
bitmap_addbit(arg->bitmap_pair->diff_bitmap, drrf->drr_offset, drrf->drr_length);
}
return (0);
}
/*
* Given the separate bitmaps for writes and frees, post-process as follows:
*
* - if the clone1_holes_file is available, filter the old holes (clone1_holes)
* out from the new holes bitmap (free_bitmap)
* - consolidate the writes and holes bitmaps into the diff_bitmap
*/
int
post_process_bitmap_pair_zst(ram_bitmap_pair_t *bitmap_pair,
const char* clone1_holes_file)
{
struct stat cbstat;
int rc = 0;
ram_bitmap_t *clone1_holes = NULL, *consolidated_holes = NULL;
if ((consolidated_holes = bitmap_dup(bitmap_pair->free_bitmap)) == NULL)
return (-1);
if (debug) {
fprintf(stdout, "Writes bitmap as calculated:\n");
bitmap_show(bitmap_pair->diff_bitmap, ~0ULL);
fprintf(stdout, "Holes bitmap as calculated:\n");
bitmap_show(bitmap_pair->free_bitmap, ~0ULL);
}
do {
if (stat(clone1_holes_file, &cbstat) == 0) {
if ((clone1_holes = bitmap_read(clone1_holes_file)) == NULL)
break;
if (debug) {
fprintf(stdout, "Clone1 holes bitmap as specified:\n");
bitmap_show(clone1_holes, ~0ULL);
}
/*
* consolidate the holes by adding old consolidated holes and
* new holes, then subtracting the writes (in the common code
* path below)
*/
if ((rc = bitmap_add(consolidated_holes, clone1_holes,
bitmap_pair->free_bitmap)))
break;
if (debug) {
fprintf(stdout, "Consolidated holes before subtracting writes:\n");
bitmap_show(consolidated_holes, ~0ULL);
}
/*
* calculate new holes by subtracting old consolidated holes
* from the calculated new holes
*/
if ((rc = bitmap_subtract(bitmap_pair->free_bitmap,
bitmap_pair->free_bitmap, clone1_holes)))
break;
if (debug) {
fprintf(stdout, "New holes (calculated - old holes):\n");
bitmap_show(bitmap_pair->free_bitmap, ~0ULL);
}
bitmap_destroy(clone1_holes);
clone1_holes = NULL;
}
/*
* subtract the new writes from the consolidated holes
*/
if ((rc = bitmap_subtract(consolidated_holes, consolidated_holes,
bitmap_pair->diff_bitmap)))
break;
/*
* at this point, the consolidated_holes are new consolidated holes, and
* the bitmap_pair->free_bitmap is new holes with old ones filtered out
*
* now calculate the diff bitmap by OR-ing bitmap_pair->diff_bitmap and
* the filtered bitmap_pair->free_bitmap
*/
if ((rc = bitmap_add(bitmap_pair->diff_bitmap, bitmap_pair->diff_bitmap,
bitmap_pair->free_bitmap)))
break;
/*
* now replace free_bitmap in the pair with the new consolidated bitmap
*/
bitmap_destroy(bitmap_pair->free_bitmap);
bitmap_pair->free_bitmap = consolidated_holes;
if (debug) {
fprintf(stdout, "Diff bitmap post-processed:\n");
bitmap_show(bitmap_pair->diff_bitmap, ~0ULL);
fprintf(stdout, "Holes bitmap post-processed:\n");
bitmap_show(bitmap_pair->free_bitmap, ~0ULL);
}
} while (0);
/* cleanup */
if (rc) {
if (clone1_holes)
bitmap_destroy(clone1_holes);
if (consolidated_holes)
bitmap_destroy(consolidated_holes);
}
return (rc);
}
/*
* The function calculates "GRAIN"-wise differential bitmap, and optionally,
* generates bitmap of holes reported in the zfs send stream, between two zfs
* snapshots. The parent and child names below are device names of the clones
* in the format 'poolname/volname'; the clones' 'origin'
* property contains the snapshot names to be diffed.
* Therefore, proceed as follows:
* obtain the snapshot names (snap1 - parent, snap2 - child)
* call zfs send -Bi snap1 snap2 | diff_fd
* read stream records form diff_fd and fill out the (initially empty) bitmap(s)
*/
ram_bitmap_pair_t *
generate_bitmap_zst(const char *parent, const char *child, int separate_bitmaps,
const char* clone1_holes_file)
{
libzfs_handle_t *g_zfs = NULL;
zfs_handle_t *parent_zhp = NULL, *child_zhp = NULL;
zst_handle_t *hdl = NULL;
zprop_source_t src;
char parent_origin[ZFS_MAXNAMELEN];
char child_origin[ZFS_MAXNAMELEN];
char buffer[64 + 2 * ZFS_MAXNAMELEN];
int diff_fd = -1, i;
FILE *diff_fp = NULL;
uint64_t parent_volsize = ~0ULL, child_volsize = ~0ULL;
uint64_t blocksize = ~0ULL;
zst_callback_descr_t zc[3];
ram_bitmap_pair_t *bitmap_pair = NULL;
zst_callback_arg_t arg;
int rc = 0;
/* init libzfs handle */
if ((g_zfs = libzfs_init()) == NULL) {
fprintf(stderr, "Failed to initialize libzfs handle\n");
exit(-1);
}
/* must be zvols */
if ((parent_zhp = zfs_open(g_zfs, parent, ZFS_TYPE_VOLUME)) == NULL ||
(child_zhp = zfs_open(g_zfs, child, ZFS_TYPE_VOLUME)) == NULL) {
fprintf(stderr, "Parent %s or child %s is not a volume\n",
parent, child);
exit(-1);
}
/* get origin and size properties */
(void) zfs_prop_get(parent_zhp, ZFS_PROP_ORIGIN, parent_origin,
ZFS_MAXNAMELEN, &src, NULL, 0, B_FALSE);
(void) zfs_prop_get(child_zhp, ZFS_PROP_ORIGIN, child_origin,
ZFS_MAXNAMELEN, &src, NULL, 0, B_FALSE);
(void) zfs_prop_get(parent_zhp, ZFS_PROP_VOLSIZE, buffer,
ZFS_MAXNAMELEN, &src, NULL, 0, B_TRUE);
sscanf(buffer, "%" PRIu64 "", &parent_volsize);
(void) zfs_prop_get(child_zhp, ZFS_PROP_VOLSIZE, buffer,
ZFS_MAXNAMELEN, &src, NULL, 0, B_TRUE);
sscanf(buffer, "%" PRIu64 "", &child_volsize);
(void) zfs_prop_get(child_zhp, ZFS_PROP_VOLBLOCKSIZE, buffer,
ZFS_MAXNAMELEN, &src, NULL, 0, B_TRUE);
sscanf(buffer, "%" PRIu64 "", &blocksize);
/* done with the datasets */
zfs_close(parent_zhp);
zfs_close(child_zhp);
libzfs_fini(g_zfs);
/* must be a snapshot name */
if (NULL == strchr(parent_origin, '@') ||
NULL == strchr(child_origin, '@')) {
fprintf(stderr, "Origin of parent %s or child %s (%s/%s) is not a "
"snapshot\n", parent, child, parent_origin, child_origin);
}
if (parent_volsize > child_volsize) {
fprintf(stderr, "Parent volume size 0x%" PRIx64 " cannot be larger "
"than child volume size 0x%" PRIx64 "\n",
parent_volsize, child_volsize);
exit(-1);
}
/* create the bitmap */
bitmap_pair = bitmap_pair_create(child_volsize, grainsize);
if (bitmap_pair == NULL) {
fprintf(stderr, "Failed to create bitmaps, not enough memory\n");
exit(-1);
}
if (separate_bitmaps == 0) {
/* free the bitmap to indicate consolidated bitmap processing */
bitmap_destroy(bitmap_pair->free_bitmap);
bitmap_pair->free_bitmap = NULL;
}
/* build callback context argument */
arg.bitmap_pair = bitmap_pair;
arg.parent_volsize = parent_volsize;
arg.child_volsize = child_volsize;
/* run zfs send and get the output in a pipe */
(void) snprintf(buffer, sizeof(buffer), "zfs send -B -i %s %s",
parent_origin, child_origin);
diff_fp = popen(buffer, "r");
if (NULL == diff_fp) {
fprintf(stderr, "failed to run %s\n", buffer);
exit(-1);
}
diff_fd = fileno(diff_fp);
if (diff_fd == -1) {
fprintf(stderr, "invaid file descriptor from popen(%s)\n", buffer);
exit(-1);
}
/* init send stream traversal handle */
if ((hdl = zst_init(diff_fd)) == NULL) {
fprintf(stderr, "zst_init() failed, not enough memory\n");
exit(-1);
}
/* register traversal callbacks */
zc[0].rtype = DRR_WRITE;
zc[0].cb = write_callback;
zc[0].arg = &arg;
zc[1].rtype = DRR_WRITE_EMBEDDED;
zc[1].cb = write_embedded_callback;
zc[1].arg = &arg;
zc[2].rtype = DRR_FREE;
zc[2].cb = free_callback;
zc[2].arg = &arg;
for (i = 0; i < 3; i++) {
if (zst_register_callback(hdl, &zc[i]) < 0) {
fprintf(stderr, "zst_register_callback() failed\n");
exit(-1);
}
}
/* invoke traversal */
if ((rc = zst_traverse(hdl))) {
fprintf(stderr, "zst_traverse() error: %d\n", rc);
}
/* fini handle */
if (zst_fini(hdl) < 0) {
fprintf(stderr, "zsf_fini() error\n");
exit(-1);
}
/* done with the pipe */
(void) pclose(diff_fp);
if (rc) {
bitmap_pair_destroy(bitmap_pair);
return (NULL);
}
if (separate_bitmaps) {
/*
* make sure that the grain is less or equal to the block size
* if using the separate bitmap optimization
*/
if (blocksize % grainsize) {
fprintf(stderr,
"cannot use separate bitmaps when blocksize %" PRIu64
" is not a multiple of grainsize %d\n",
blocksize, grainsize);
exit(-1);
}
rc = post_process_bitmap_pair_zst(bitmap_pair, clone1_holes_file);
if (rc) {
fprintf(stderr, "bitmap post-process error\n");
exit(-1);
}
}
return (bitmap_pair);
}
int
create_bitmap_zst(const char *parent, const char *child,
const char *bitmap, int separate_bitmaps,
const char *clone1_holes_file)
{
ram_bitmap_pair_t *bitmap_pair = NULL;
if ((bitmap_pair = generate_bitmap_zst(parent, child,
separate_bitmaps,
clone1_holes_file)) == NULL)
return (-1);
if (separate_bitmaps) {
/*
* get path for the new holes file - 'bitmap-path'.holes\0
* write out the new holes
*/
char *new_path = malloc(strlen(bitmap)+strlen(".holes")+1);
strcpy(new_path, bitmap);
strcat(new_path, ".holes");
holemap_write(bitmap_pair->free_bitmap, new_path);
free(new_path);
}
bitmap_write(bitmap_pair->diff_bitmap, bitmap);
bitmap_pair_destroy(bitmap_pair);
return (0);
}
/* Bitmap verification modes */
ram_bitmap_t *
generate_bitmap_raw(const char *parent, const char *child, uint64_t byte_offset, uint64_t byte_length)
{
enum { PARENT, CHILD, DONE};
int fd[DONE];
ram_bitmap_t *diff_bitmap = NULL;
libzfs_handle_t *g_zfs = NULL;
zfs_handle_t *parent_zhp = NULL, *child_zhp = NULL;
zprop_source_t src;
char buffer[64 + ZFS_MAXNAMELEN];
uint64_t parent_volsize = ~0ULL, child_volsize = ~0ULL;
uint64_t i = 0;
/* round down start offset */
uint64_t grain_offset_start = byte_offset / grainsize;
/* round up end offset */
uint64_t grain_offset_end = (byte_offset + byte_length + grainsize - 1) / grainsize;
/* init libzfs handle */
if ((g_zfs = libzfs_init()) == NULL) {
fprintf(stderr, "Failed to initialize libzfs handle\n");
exit(-1);
}
/* must be zvols */
if ((parent_zhp = zfs_open(g_zfs, parent, ZFS_TYPE_VOLUME)) == NULL ||
(child_zhp = zfs_open(g_zfs, child, ZFS_TYPE_VOLUME)) == NULL) {
fprintf(stderr, "Parent %s or child %s is not a volume\n",
parent, child);
exit(-1);
}
/* get size properties */
(void) zfs_prop_get(parent_zhp, ZFS_PROP_VOLSIZE, buffer,
ZFS_MAXNAMELEN, &src, NULL, 0, B_TRUE);
sscanf(buffer, "%" PRIu64 "", &parent_volsize);
(void) zfs_prop_get(child_zhp, ZFS_PROP_VOLSIZE, buffer,
ZFS_MAXNAMELEN, &src, NULL, 0, B_TRUE);
sscanf(buffer, "%" PRIu64 "", &child_volsize);
/* done with the datasets */
zfs_close(parent_zhp);
zfs_close(child_zhp);
libzfs_fini(g_zfs);
if (parent_volsize > child_volsize) {
fprintf(stderr, "Parent volume size 0x%" PRIx64 " cannot be larger "
"than child volume size 0x%" PRIx64 "\n",
parent_volsize, child_volsize);
exit(-1);
}
/* volume sizes should be integer number of grains */
if (parent_volsize % grainsize || child_volsize % grainsize) {
fprintf(stderr, "Parent volume size 0x%" PRIx64
"or child volume size 0x%" PRIx64
"is not a multiple of grain size 0x%x\n",
parent_volsize, child_volsize, grainsize);
exit(-1);
}
/* create the bitmap */
diff_bitmap = bitmap_create(child_volsize, grainsize);
if (diff_bitmap == NULL) {
fprintf(stderr, "Failed to create bitmap, not enough memory\n");
return (NULL);
}
/* open parent and child */
sprintf(buffer, "/dev/zvol/%s", parent);
if ((fd[PARENT] = open(buffer, O_RDONLY)) < 0) {
sprintf(buffer, "parent %s open() failed", parent);
perror(buffer);
exit(-1);
}
sprintf(buffer, "/dev/zvol/%s", child);
if ((fd[CHILD] = open(buffer, O_RDONLY)) < 0) {
sprintf(buffer, "child %s open() failed", child);
perror(buffer);
exit(-1);
}
/* read grains from parent and child and memcmp() those */
for (i = 0; i < parent_volsize/grainsize; i++) {
char dbuffer[DONE][grainsize];
ssize_t rd, rc;
int a;
/* only interested in one specific byte range */
if ((byte_offset != ~0ULL) &&
(i < grain_offset_start || i >= grain_offset_end))
continue;
for (a = PARENT; a < DONE; a++) {
for (rd = 0; rd < grainsize; rd += rc) {
rc = pread(fd[a], &(dbuffer[a][rd]), grainsize, i * grainsize);
if (rc <= 0)
break;
}
if (rd < grainsize) {
fprintf(stderr, "read() of %s failed",
(a == PARENT) ? "parent" : "child");
exit(-1);
}
}
if (memcmp(&(dbuffer[PARENT][0]), &(dbuffer[CHILD][0]), grainsize)) {
/* grain is different, add bit to the bitmap */
bitmap_addbit(diff_bitmap, i*grainsize, grainsize);
}
}
/*
* if child is larger than the parent, all but zero-filled grains beyond the
* parent size are different; the latter is due to the sparse nature of zvols
* that are used
*/
if (i < child_volsize/grainsize) {
char zeros[grainsize];
memset(zeros, 0, grainsize);
for (; i < child_volsize/grainsize; i++) {
char dbuffer[grainsize];
ssize_t rd, rc;
for (rd = 0; rd < grainsize; rd += rc) {
rc = read(fd[CHILD], &(dbuffer[rd]), grainsize);
if (rc <= 0)
break;
}
if (rd < grainsize) {
fprintf(stderr, "read() of child failed");
exit(-1);
}
if (memcmp(&(dbuffer[0]), &(zeros[0]), grainsize)) {
/* grain is different, add bit to the bitmap */
bitmap_addbit(diff_bitmap, i*grainsize, grainsize);
}
}
}
close(fd[PARENT]);
close(fd[CHILD]);
return (diff_bitmap);
}
/*
* Verify the bitmap generation code by comparing a bitmap generated with
* ZST based approaches with a raw bitmap generated by bitwise comparison
* of grain size blocks of the parent and the child
*/
int verify_bitmaps(const char *parent, const char *child, uint64_t byte_offset,
uint64_t byte_length, int separate_bitmaps,
const char* clone1_holes)
{
ram_bitmap_pair_t *bmap_pair_zst = NULL;
ram_bitmap_t *bmap_raw = NULL;
int rc = 0;
printf("Verifying ZST based bitmap by bitwise comparison of the volumes\n");
bmap_pair_zst = generate_bitmap_zst(parent, child, separate_bitmaps,
clone1_holes);
bmap_raw = generate_bitmap_raw(parent, child, byte_offset, byte_length);
/* Make sure the grainsize argument is correct */
if (grainsize != bmap_pair_zst->diff_bitmap->mqhdr.grain_size_lbas*LBAS_SIZE) {
printf("Invalid grain size %u specified - the bitmap uses %u\n", grainsize,
bmap_pair_zst->diff_bitmap->mqhdr.grain_size_lbas*LBAS_SIZE);
return (EINVAL);
}
if (byte_offset == ~0ULL) {
/* generate/verify whole bitmaps */
uint64_t
raw_bits = bitmap_get_bitcount(bmap_raw),
write_bits = bitmap_get_bitcount(bmap_pair_zst->diff_bitmap),
free_bits = (bmap_pair_zst->free_bitmap) ?
bitmap_get_bitcount(bmap_pair_zst->free_bitmap) : 0;
printf("Summary: %s has %"PRIi64" bits set, "
"%s has %"PRIi64" bits set, "
"%s has %"PRIi64" bits set\n",
"raw bitmap", raw_bits,
"write bitmap", write_bits,
"free bitmap", free_bits);
/*
* compare - in this comparison, we are looking to make sure that the
* the second bitmap is not missing any bits set in the first bitmap
*/
printf("Verifying ZST based bitmap against raw bitmap\n");
rc = bitmap_compare(bmap_raw, bmap_pair_zst->diff_bitmap);
if (rc)
printf("Error - missing bits in ZST bitmap\n");
else
printf("ZST bitmap has at least as many bits as "
"raw bitmap\n");
} else {
/* grain range check */
uint64_t i;
printf("%24s %5s %5s %5s\n", "Byte offset", "Diff", "Raw", "Hole");
for (i = byte_offset; i < (byte_offset + byte_length); i+=grainsize) {
int zst_bit = bitmap_get_bit(bmap_pair_zst->diff_bitmap, i);
int raw_bit = bitmap_get_bit(bmap_raw, i);
int hole_bit = bitmap_get_bit(bmap_pair_zst->diff_bitmap, i);
printf("0x%-21"PRIx64" %5d %5d %5d\n", i, zst_bit, raw_bit, hole_bit);
}
}
if (rc) {
/* write out the bitmaps for further analysis */
printf("Bitmaps are saved for further analysis in /tmp/bitmap.*\n");
bitmap_write(bmap_pair_zst->diff_bitmap, "/tmp/bitmap.diff");
if (bmap_pair_zst->free_bitmap)
holemap_write(bmap_pair_zst->free_bitmap, "/tmp/bitmap.hole");
bitmap_write(bmap_raw, "/tmp/bitmap.raw");
}
/* cleanup */
bitmap_pair_destroy(bmap_pair_zst);
bitmap_destroy(bmap_raw);
return (rc);
}
void show_bitmap(char *bitmap_file, uint64_t offset)
{
ram_bitmap_t *bitmap = bitmap_read(bitmap_file);
if (bitmap == NULL)
exit(-1);
bitmap_show(bitmap, offset);
bitmap_destroy(bitmap);
}
int main(int argc, char **argv)
{
int rc = 0, opt, index = 0, separate_bitmaps = 0;
char *clone1_holes = NULL;
/* process getopt-style arguments */
while ((opt = getopt(argc, argv, "hvzdg:c:")) != -1) {
switch (opt) {
case 'h':
usage();
return (0);
break;
case 'v':
verbosity++;
index++;
break;
case 'd':
debug++;
index++;
break;
case 'g':
index++;
grainsize = (unsigned)atoi(optarg);
index++;
break;
case 'z':
separate_bitmaps = 1;
index++;
break;
case 'c':
index++;
clone1_holes = optarg;
index++;
break;
case '?':
usage();
fprintf(stderr, "invalid argument %c\n", optopt);
return (-1);
}
}
/* fast-forward the arguments */
argc -= index;
argv += index;
if (argc < 3)
return (usage());
if (separate_bitmaps == 0 && clone1_holes)
return (usage());
do {
int mask;
for (mask = grainsize - 1; (mask && (mask & 1)); mask >>= 1)
;
if (mask) {
fprintf(stderr, "grainsize %u is not a power of two\n", grainsize);
}
} while (0);
if ((strcmp (argv[1], "bitmap") == 0) ||
(strcmp (argv[1], "zst-bitmap") == 0)) {
/*
* invoke ZFS send-based bitmap generation code
*/
if (argc != 5)
return (usage());
rc = create_bitmap_zst(argv[2], argv[3], argv[4],
separate_bitmaps, clone1_holes);
if (rc) {
fprintf(stdout, "failed to generate bitmap, error %d\n", rc);
}
}
else if (strcmp (argv[1], "bitmap-verify") == 0) {
uint64_t offset = ~0ULL, length = 0ULL;
if (argc < 4)
return (usage());
if (argc > 4)
sscanf(argv[4], "%"PRIi64"", &offset);
if (argc > 5)
sscanf(argv[5], "%"PRIi64"", &length);
rc = verify_bitmaps(argv[2], argv[3], offset, length,
separate_bitmaps, clone1_holes);
if (rc) {
fprintf(stdout, "failed to verify bitmap, error %d\n", rc);
}
}
else if (strcmp (argv[1], "bitmap-show") == 0) {
uint64_t offset = ~0ULL;
if (argc < 3)
return (usage());
if (argc > 3)
sscanf(argv[3], "%"PRIi64"", &offset);
show_bitmap(argv[2], offset);
}
else return usage();
return (rc);
}