| /* ============================================================ |
| * Copyright (c) 2014 Actifio Inc. All Rights Reserved |
| * ============================================================= |
| */ |
| |
| #include <stdlib.h> |
| #include <assert.h> |
| #include <sys/stat.h> |
| #include <errno.h> |
| #include <stdbool.h> |
| #include <fcntl.h> |
| |
| #include "bitmap.h" |
| |
| extern int verbosity; |
| |
| #define COMPRESSION_LEVEL 6 |
| |
| ram_bitmap_t *bitmap_create(uint64_t volsize, unsigned int grainsize) |
| { |
| uint64_t size = sizeof(fc_bitmap_hdr_t) + sizeof(fc_bitmap_mq_hdr_t); |
| /* Roundup to grainsize and to the next byte */ |
| uint64_t bmsize = (((volsize + grainsize - 1) / grainsize) + 8 - 1) / 8; |
| |
| if (volsize % LBAS_SIZE || grainsize % LBAS_SIZE) { |
| printf("Invalid alignment: volsize/grainsize %"PRIi64"/%u " |
| "should be multible of LBAS %u\n", volsize, grainsize, LBAS_SIZE); |
| return NULL; |
| } |
| if (volsize % grainsize) { |
| printf("Invalid alignment: volsize %"PRIi64" should be multible of grainsize %u\n", |
| volsize, grainsize); |
| return NULL; |
| } |
| size += bmsize; |
| |
| ram_bitmap_t * bitmap = (ram_bitmap_t *) malloc(size); |
| |
| assert(bitmap); |
| memset(bitmap,0,size); |
| |
| bitmap->mqhdr.grain_size_lbas = grainsize/LBAS_SIZE; |
| bitmap->mqhdr.vdisk_size_lbas = volsize/LBAS_SIZE; |
| bitmap->mqhdr.bitmap_size_bytes = bmsize; |
| |
| bitmap->mqhdr.bitmap_mq_magic[0] = ((FC_BITMAP_MQ_MAGIC >> 24) & 0xFF); |
| bitmap->mqhdr.bitmap_mq_magic[1] = ((FC_BITMAP_MQ_MAGIC >> 16) & 0xFF); |
| bitmap->mqhdr.bitmap_mq_magic[2] = ((FC_BITMAP_MQ_MAGIC >> 8) & 0xFF); |
| bitmap->mqhdr.bitmap_mq_magic[3] = (FC_BITMAP_MQ_MAGIC & 0xFF); |
| |
| // Let's make sure we did this right. |
| assert( bitmap->mqhdr.bitmap_size_bytes == bmsize); |
| |
| return bitmap; |
| } |
| |
| ram_bitmap_t *bitmap_dup(ram_bitmap_t *bitmap) |
| { |
| ram_bitmap_t *new_bitmap = |
| bitmap_create(bitmap->mqhdr.vdisk_size_lbas*LBAS_SIZE, |
| bitmap->mqhdr.grain_size_lbas*LBAS_SIZE); |
| if (new_bitmap) |
| bcopy(&(bitmap->bits[0]), &(new_bitmap->bits[0]), |
| bitmap->mqhdr.bitmap_size_bytes); |
| |
| return (new_bitmap); |
| } |
| |
| void bitmap_destroy(ram_bitmap_t *bitmap) |
| { |
| free(bitmap); |
| } |
| |
| ram_bitmap_pair_t *bitmap_pair_create(uint64_t volsize, unsigned int grainsize) |
| { |
| ram_bitmap_pair_t *pair = malloc(sizeof(ram_bitmap_pair_t)); |
| if (pair == NULL) |
| return NULL; |
| |
| pair->diff_bitmap = bitmap_create(volsize, grainsize); |
| if (pair->diff_bitmap == NULL) { |
| free(pair); |
| return NULL; |
| } |
| |
| pair->free_bitmap = bitmap_create(volsize, grainsize); |
| if (pair->free_bitmap == NULL) { |
| free(pair->diff_bitmap); |
| free(pair); |
| return NULL; |
| } |
| |
| return pair; |
| } |
| |
| void bitmap_pair_destroy(ram_bitmap_pair_t *pair) |
| { |
| if (pair) { |
| if (pair->free_bitmap) |
| bitmap_destroy(pair->free_bitmap); |
| if (pair->diff_bitmap) |
| bitmap_destroy(pair->diff_bitmap); |
| free(pair); |
| } |
| } |
| |
| void bitmap_addbit(ram_bitmap_t *diffbitmap, uint64_t rangeaddr, uint64_t rangesize) |
| { |
| /* |
| * Get the (first) grain that contains rangeaddr, get the (last) grain that contains |
| * rangeadd+rangesize, then walk through the range of grains including the last |
| * changed grain and set the bits |
| */ |
| uint64_t start_block = rangeaddr/(diffbitmap->mqhdr.grain_size_lbas * LBAS_SIZE); |
| uint64_t end_block = (rangeaddr + rangesize - 1)/(diffbitmap->mqhdr.grain_size_lbas * LBAS_SIZE); |
| uint64_t b; |
| |
| for (b = start_block; b <= end_block; b++) { |
| uint64_t byte_idx = b / 8; |
| int bit_idx = b % 8; |
| diffbitmap->bits[byte_idx] |= (uint8_t)(1 << bit_idx); |
| } |
| } |
| |
| void bitmap_fwrite(const void *ptr, size_t size, size_t nmemb, |
| FILE *stream) |
| { |
| if (fwrite(ptr, size, nmemb, stream) != nmemb) |
| { |
| fprintf(stderr,"Error: cannot write to bitmap file\n"); |
| exit(-1); |
| } |
| } |
| |
| |
| bool is_buffer_zeroed(unsigned char *buffer, uint64_t size) |
| { |
| return !(bool)(*buffer || memcmp(buffer, buffer + 1, (size_t)size - 1)); |
| } |
| |
| /* This routine will keep calling pwrite until all the bytes in a block gets written. |
| * Subsequent pwrite calls will attempt to write only the remaining bytes. |
| * At any point of time if pwrite returns a value <= 0, error will be thrown. |
| */ |
| int pwrite_data(const int fd, const void *buf, size_t size, off_t write_offset) |
| { |
| off_t read_offset = 0; |
| int rem = size; |
| while (rem) { |
| int nbytes = pwrite(fd, buf + read_offset, rem, write_offset); |
| if (nbytes <= 0) |
| return -1; |
| rem -= nbytes; |
| read_offset += nbytes; |
| write_offset += nbytes; |
| } |
| return 0; |
| } |
| /* |
| * Write bitmap portion (without headers) of ram_bitmap_t in sparse manner |
| */ |
| int bitmap_write_sparse(const int fd, uint64_t bitmap_size_bytes, unsigned char *buf) |
| { |
| ftruncate(fd, (FC_BITMAP_BITMAP_OFFSET + bitmap_size_bytes)); |
| int64_t i, remainder, chunks; |
| off_t read_offset = 0; |
| off_t write_offset = FC_BITMAP_BITMAP_OFFSET; |
| chunks = bitmap_size_bytes / LBAS_SIZE; |
| remainder = bitmap_size_bytes % LBAS_SIZE; |
| |
| for (i = 0; i < chunks; i++) { |
| if (!(is_buffer_zeroed(&buf[read_offset], LBAS_SIZE))) { |
| if (pwrite_data(fd, buf + read_offset, LBAS_SIZE, write_offset) < 0) |
| return -1; |
| } |
| read_offset += LBAS_SIZE; |
| write_offset += LBAS_SIZE; |
| } |
| |
| if (remainder) { |
| if (pwrite_data(fd, buf + read_offset, remainder, write_offset) < 0) |
| return -1; |
| } |
| return 0; |
| } |
| |
| void bitmap_write(ram_bitmap_t *diff_bitmap, const char * bitmap) |
| { |
| struct stat sb; |
| int fd = open(bitmap, O_CREAT|O_WRONLY, 0666); |
| if(fd < 0) { |
| fprintf(stderr,"Error: cannot create bitmap file %s: %s\n", bitmap, strerror(errno)); |
| exit(-1); |
| } |
| |
| if(fstat(fd, &sb)) { |
| fprintf(stderr,"Error: cannot retrieve file info %s: %s\n", bitmap, strerror(errno)); |
| close(fd); |
| exit(-1); |
| } |
| /* write out the bm header only if this is a new file |
| * for existing bitmap, bmhdr is already up to date, we just need to update mqhdr and |
| * the actual bitmap |
| */ |
| if(!sb.st_size && (pwrite_data(fd, &diff_bitmap->bmhdr, sizeof(diff_bitmap->bmhdr), 0) < 0)) |
| goto write_fail; |
| |
| /* mqhdr needs to be updated even if it is an already existing file. Bitmap size (initially 0) |
| * and other fields of mqhdr will be modified here |
| */ |
| if (pwrite_data(fd, &diff_bitmap->mqhdr, sizeof(diff_bitmap->mqhdr), FC_BITMAP_MQ_HDR_OFFSET) < 0) |
| goto write_fail; |
| |
| if (bitmap_write_sparse(fd, diff_bitmap->mqhdr.bitmap_size_bytes, diff_bitmap->bits) < 0) |
| goto write_fail; |
| |
| if (close(fd)) { |
| fprintf(stderr, "Error: unable to close fd %s: %s\n", bitmap, strerror(errno)); |
| exit(-1); |
| } |
| return; |
| |
| write_fail: |
| fprintf(stderr, "Error: unable to write bitmap %s : %s\n", bitmap, strerror(errno)); |
| close(fd); |
| exit(-1); |
| } |
| |
| extern size_t gzip_compress(void *src, void *dst, size_t s_len, size_t d_len, int level); |
| extern int gzip_decompress(void *s_start, void *d_start, size_t s_len, size_t d_len, int n); |
| |
| /* |
| * Because of runs of 1s, holemap files don't sparsify well. |
| * This function will compress the bitmap portion of ram_bitmap_t before |
| * persisting it to the disk |
| */ |
| void holemap_write(ram_bitmap_t *free_bitmap, const char * holemap) |
| { |
| int exists = 0; |
| struct stat sb; |
| size_t nread; |
| FILE *bm; |
| unsigned char * compressed_bitmap; |
| |
| u_int64_t size = free_bitmap->mqhdr.bitmap_size_bytes; |
| compressed_bitmap = (unsigned char *) malloc(size); |
| assert(compressed_bitmap); |
| memset(compressed_bitmap,0,size); |
| |
| /* In some cases compression output is worse than input, and thus it won't fit. This will cause the |
| * library to return failure. gzip_compress() indicates this failure by returning original input size. |
| * However in failure case, gzip_compress() simply copies data from src to dest buffer if s_len, d_len |
| * parameters have the same value. Therefore to avoid unnecessary copying, dst_len = src-len - 1 is |
| * being passed. |
| * If return value of gzip_compress() equals original size, it'll be identified as error scenario and |
| * original uncompressed bitmap will be written to the disk. |
| */ |
| u_int64_t dest_size = gzip_compress(free_bitmap->bits, compressed_bitmap, size, size-1, COMPRESSION_LEVEL); |
| |
| if(stat(holemap, &sb)==0) |
| exists = 1; |
| |
| if (exists) { |
| bm = fopen(holemap, "r+b"); |
| if(!bm) { |
| fprintf(stderr,"Error: cannot open holemap file %s: %s\n", holemap, strerror(errno)); |
| free(compressed_bitmap); |
| exit(-1); |
| } |
| fseek(bm, 0, size); |
| nread = fread(&free_bitmap->bmhdr, sizeof(free_bitmap->bmhdr), 1, bm); |
| } else { |
| bm = fopen(holemap, "w+b"); |
| if(!bm) { |
| fprintf(stderr,"Error: cannot create holemap file %s: %s\n", holemap, strerror(errno)); |
| free(compressed_bitmap); |
| exit(-1); |
| } |
| } |
| |
| if (dest_size != size) { |
| /* compressed length < source length |
| * i.e. compression was successful |
| */ |
| free_bitmap->bmhdr.compressed_size = dest_size; |
| free_bitmap->bmhdr.is_compressed = 1; |
| fseek(bm, 0, SEEK_SET); |
| bitmap_fwrite(&free_bitmap->bmhdr, sizeof(free_bitmap->bmhdr), 1, bm); |
| bitmap_fwrite(&free_bitmap->mqhdr, sizeof(free_bitmap->mqhdr), 1, bm); |
| fseek(bm, FC_BITMAP_BITMAP_OFFSET, SEEK_SET); |
| /* write compressed bitmap */ |
| bitmap_fwrite(compressed_bitmap, dest_size, 1, bm); |
| } else { |
| /* compressed length = source length: this can happen only |
| * in case of failure |
| */ |
| fseek(bm, 0, SEEK_SET); |
| bitmap_fwrite(&free_bitmap->bmhdr, sizeof(free_bitmap->bmhdr), 1, bm); |
| bitmap_fwrite(&free_bitmap->mqhdr, sizeof(free_bitmap->mqhdr), 1, bm); |
| fseek(bm, FC_BITMAP_BITMAP_OFFSET, SEEK_SET); |
| /* write uncompressed bitmap */ |
| bitmap_fwrite(free_bitmap->bits, size, 1, bm); |
| } |
| |
| free(compressed_bitmap); |
| if (fclose(bm)) { |
| fprintf(stderr, "Error: unable to close file %s : %s\n", holemap, strerror(errno)); |
| exit(-1); |
| } |
| } |
| |
| /* |
| * Read bitmap/holemap file from disk. |
| * holemap files will be decompressed before populating ram_bitmap_t |
| */ |
| ram_bitmap_t *bitmap_read(const char *bitmap) |
| { |
| size_t nread, compressed_size; |
| int ret; |
| unsigned char * compressed_bitmap = NULL; |
| fc_bitmap_mq_hdr_t mqhdr; |
| ram_bitmap_t *ram_bitmap = NULL; |
| FILE *bm = fopen(bitmap, "r"); |
| |
| if (!bm) |
| goto fail; |
| |
| /* get the size of the bitmap */ |
| fseek(bm, FC_BITMAP_MQ_HDR_OFFSET, SEEK_SET); |
| nread = fread(&mqhdr, sizeof(mqhdr), 1, bm); |
| if (nread != 1) |
| goto fail; |
| |
| /* create the bitmap */ |
| if ((ram_bitmap = bitmap_create(mqhdr.vdisk_size_lbas*LBAS_SIZE, |
| mqhdr.grain_size_lbas*LBAS_SIZE)) == NULL) |
| goto fail; |
| |
| /* |
| * now that the properly sized bitmap is created, read it in |
| * first, read the headers |
| * then, read the bits |
| */ |
| fseek(bm, 0, SEEK_SET); |
| nread = fread (&ram_bitmap->bmhdr, sizeof(ram_bitmap->bmhdr), 1, bm); |
| if (nread != 1) |
| goto fail; |
| |
| /* |
| * this is initialized by bitmap_create(), but still rewrite, |
| * just to make sure the header is exactly the same as on disk |
| */ |
| ram_bitmap->mqhdr = mqhdr; |
| /* read in the bitmap bits */ |
| fseek(bm, FC_BITMAP_BITMAP_OFFSET, SEEK_SET); |
| |
| /* check if bitmap needs to be decompressed before storing*/ |
| if (ram_bitmap->bmhdr.is_compressed) { |
| compressed_size = ram_bitmap->bmhdr.compressed_size; |
| compressed_bitmap = (unsigned char *) malloc(compressed_size); |
| nread = fread(compressed_bitmap, compressed_size, 1, bm); |
| if (nread != 1) |
| goto fail; |
| ret = gzip_decompress(compressed_bitmap, ram_bitmap->bits, compressed_size, |
| ram_bitmap->mqhdr.bitmap_size_bytes, COMPRESSION_LEVEL); |
| if (ret < 0) { |
| fprintf(stderr,"Error: holemap file decompression failed\n"); |
| goto fail; |
| } |
| free(compressed_bitmap); |
| } else { |
| nread = fread(ram_bitmap->bits, ram_bitmap->mqhdr.bitmap_size_bytes, 1, bm); |
| if (nread != 1) |
| goto fail; |
| } |
| fclose(bm); |
| |
| return ram_bitmap; |
| fail: |
| perror("Failed to read bitmap"); |
| if (bm) |
| fclose(bm); |
| if (ram_bitmap) |
| free(ram_bitmap); |
| if (compressed_bitmap) |
| free(compressed_bitmap); |
| return (NULL); |
| } |
| |
| |
| /* |
| * Produce a result bitmap which is a logical OR of the |
| * left- and righ-hand-side bitmap arguments |
| * the lhs and rhs arguments can be of different sizes, |
| * but the result has to be of the largest size |
| */ |
| int bitmap_add(ram_bitmap_t *result, ram_bitmap_t *lhs, ram_bitmap_t *rhs) |
| { |
| fc_bitmap_mq_hdr_t *hdr_res = &(result->mqhdr), |
| *hdr_lhs = &(lhs->mqhdr), *hdr_rhs = &(rhs->mqhdr); |
| unsigned char *bits_res = &(result->bits[0]), *bits_lhs = &(lhs->bits[0]), |
| *bits_rhs = &(rhs->bits[0]), *bits_remainder = NULL; |
| uint64_t i, min_size, max_size; |
| |
| /* compare grains and sizes */ |
| if (hdr_lhs->grain_size_lbas != hdr_rhs->grain_size_lbas || |
| hdr_lhs->grain_size_lbas != hdr_res->grain_size_lbas) { |
| fprintf(stderr, "different bitmap grain size detected," |
| " cannot add bitmaps\n"); |
| return (-1); |
| } |
| |
| if (hdr_lhs->bitmap_size_bytes > hdr_rhs->bitmap_size_bytes) { |
| min_size = hdr_rhs->bitmap_size_bytes; |
| max_size = hdr_lhs->bitmap_size_bytes; |
| bits_remainder = bits_lhs; |
| } else { |
| min_size = hdr_lhs->bitmap_size_bytes; |
| max_size = hdr_rhs->bitmap_size_bytes; |
| bits_remainder = bits_rhs; |
| } |
| |
| if (max_size != hdr_res->bitmap_size_bytes) { |
| fprintf(stderr, "resulting bitmap is of wrong byte size," |
| " cannot add bitmaps\n"); |
| return (-1); |
| } |
| |
| /* OR the common parts of the bitmaps */ |
| for (i = 0; i < min_size; i++) |
| bits_res[i] = bits_lhs[i] | bits_rhs[i]; |
| |
| /* fill in the rest using the larger bitmap */ |
| for ( ; i < max_size; i++) |
| bits_res[i] = bits_remainder[i]; |
| |
| return (0); |
| } |
| |
| /* |
| * Produce a result bitmap which is a logical AND of the |
| * left-hand-side bitmap and the inverse of the right-hand-size bitmap |
| * We will require that the rhs bitmap is not larger than the lhs bitmap, |
| * and the lhs bitmap is not larger than the lhs bitmap |
| */ |
| int bitmap_subtract(ram_bitmap_t *result, ram_bitmap_t *lhs, ram_bitmap_t *rhs) |
| { |
| fc_bitmap_mq_hdr_t *hdr_res = &(result->mqhdr), |
| *hdr_lhs = &(lhs->mqhdr), *hdr_rhs = &(rhs->mqhdr); |
| unsigned char *bits_res = &(result->bits[0]), *bits_lhs = &(lhs->bits[0]), |
| *bits_rhs = &(rhs->bits[0]); |
| uint64_t i; |
| |
| /* compare grains and byte sizes */ |
| if (hdr_lhs->grain_size_lbas != hdr_rhs->grain_size_lbas || |
| hdr_lhs->grain_size_lbas != hdr_res->grain_size_lbas) { |
| fprintf(stderr, "different bitmap grain sizes detected," |
| " cannot subtract bitmaps\n"); |
| return (-1); |
| } |
| |
| if (hdr_lhs->bitmap_size_bytes < hdr_rhs->bitmap_size_bytes || |
| hdr_res->bitmap_size_bytes < hdr_lhs->bitmap_size_bytes) { |
| fprintf(stderr, "bitmap sizes are incompatible," |
| " cannot subtract bitmaps\n"); |
| return (-1); |
| } |
| |
| /* subtract up to the rhs size */ |
| for (i = 0; i < hdr_rhs->bitmap_size_bytes; i++) |
| bits_res[i] = bits_lhs[i] & (~bits_rhs[i]); |
| |
| /* copy up to the lhs size */ |
| for ( ; i < hdr_lhs->bitmap_size_bytes; i++) |
| bits_res[i] = bits_lhs[i]; |
| |
| return (0); |
| } |
| |
| |
| |
| /* assumes the string of length at least 9 is managed by the caller */ |
| char *bitmap_show_bits(char *string, const char bits) |
| { |
| /* print out bits 0-7 left to right */ |
| char *ptr; |
| int i; |
| for (i = 0, ptr = string; i < 8; i++, ptr++) { |
| sprintf(ptr, "%d", (bits & (1 << i)) >> i); |
| } |
| string[8] = '\0'; |
| return string; |
| } |
| |
| |
| void bitmap_show(ram_bitmap_t *bitmap, uint64_t offset) |
| { |
| uint64_t i, j, ngrains; |
| |
| if ((bitmap->mqhdr.bitmap_mq_magic[0] != ((FC_BITMAP_MQ_MAGIC >> 24) & 0xFF)) || |
| (bitmap->mqhdr.bitmap_mq_magic[1] != ((FC_BITMAP_MQ_MAGIC >> 16) & 0xFF)) || |
| (bitmap->mqhdr.bitmap_mq_magic[2] != ((FC_BITMAP_MQ_MAGIC >> 8) & 0xFF)) || |
| (bitmap->mqhdr.bitmap_mq_magic[3] != (FC_BITMAP_MQ_MAGIC & 0xFF))) { |
| fprintf(stderr, "Invalid magic number in the bitmap mq header\n"); |
| exit(-1); |
| } |
| |
| fprintf(stdout, "Bitmap header:\n"); |
| fprintf(stdout, "\tbitmap size: 0x%"PRIx64" grains\n", bitmap->mqhdr.bitmap_size_bytes*8); |
| fprintf(stdout, "\tvdisk size: 0x%"PRIx64"\n", bitmap->mqhdr.vdisk_size_lbas*LBAS_SIZE); |
| fprintf(stdout, "\tgrain size: 0x%"PRIx32"\n", bitmap->mqhdr.grain_size_lbas*LBAS_SIZE); |
| |
| if (offset != ~0ULL) { |
| /* just find the grain and tell if it is set */ |
| uint64_t grain = offset / (bitmap->mqhdr.grain_size_lbas*LBAS_SIZE); |
| fprintf(stdout, |
| "bitmap offset 0x%"PRIx64" belongs to grain 0x%"PRIx64" that is %s\n", |
| offset, grain, (bitmap->bits[grain/8] & (1 << (grain%8))) ? "set" : "cleared"); |
| return; |
| } |
| |
| ngrains = bitmap->mqhdr.bitmap_size_bytes * 8; |
| |
| fprintf(stdout, "Bitmap bitset:"); |
| for (i = 0; i < bitmap->mqhdr.bitmap_size_bytes; i++) { |
| char bit_string[9]; |
| if ((i % 8) == 0) |
| fprintf(stdout, "\n\t"); |
| fprintf(stdout, "%s ", bitmap_show_bits(bit_string, bitmap->bits[i])); |
| } |
| fprintf(stdout, "\n"); |
| |
| fprintf(stdout, "Bitmap range list:\n"); |
| for (i = 0; i < ngrains; i = j) { |
| uint64_t start, end; |
| int iset = (bitmap->bits[i/8] & (1 << (i%8))) >> (i%8); |
| /* find next cleared/set range */ |
| for (j = i; (j < ngrains); j++) { |
| int jset = (bitmap->bits[j/8] & (1 << (j%8))) >> (j%8); |
| if (jset != iset) |
| break; |
| } |
| start = bitmap->mqhdr.grain_size_lbas*LBAS_SIZE*i; |
| end = bitmap->mqhdr.grain_size_lbas*LBAS_SIZE*j; |
| fprintf(stdout, "\tbitmap range: [0x%"PRIx64",0x%"PRIx64") is %s (0x%"PRIx64" grain%s)\n", |
| start, end, (iset) ? "set" : "cleared", j - i, ((j - i) > 1) ? "s" : ""); |
| } |
| |
| return; |
| } |
| |
| |
| /* |
| * return 0 if bitmap1 is a subset of bitmap2 (or equal to it), |
| * and return -1 otherwise |
| */ |
| int bitmap_compare(ram_bitmap_t *bm1, ram_bitmap_t *bm2) |
| { |
| fc_bitmap_mq_hdr_t *hdr1 = &(bm1->mqhdr), *hdr2 = &(bm2->mqhdr); |
| unsigned char *bitmap1 = &(bm1->bits[0]), *bitmap2 = &(bm2->bits[0]); |
| uint64_t i, missing_bits = 0, extra_bits = 0; |
| |
| /* compare headers */ |
| if (hdr1->grain_size_lbas != hdr2->grain_size_lbas) { |
| fprintf(stderr, "different bitmap grain size (%d vs %d), " |
| "cannot compare bitmaps\n", |
| hdr1->grain_size_lbas, hdr2->grain_size_lbas); |
| return (-1); |
| } |
| if (hdr1->bitmap_size_bytes > hdr2->bitmap_size_bytes) { |
| uint64_t diff_first_byte = |
| hdr2->bitmap_size_bytes*8*hdr2->grain_size_lbas*LBAS_SIZE, |
| diff_last_byte = |
| hdr1->bitmap_size_bytes*8*hdr1->grain_size_lbas*LBAS_SIZE; |
| fprintf(stderr, "unexpected differences found in byte range " |
| "[0x%"PRIx64",0x%"PRIx64") (all data different) \n", |
| diff_first_byte, diff_last_byte); |
| return (-1); |
| } |
| |
| for (i = 0; i < hdr1->bitmap_size_bytes; i++) { |
| char bm_byte[2][9]; |
| uint8_t mask = (uint8_t)(bitmap1[i] & ~bitmap2[i]); |
| uint64_t diff_first_byte = |
| i*8*hdr1->grain_size_lbas*LBAS_SIZE, |
| diff_last_byte = |
| diff_first_byte + 8*hdr1->grain_size_lbas*LBAS_SIZE; |
| uint32_t grain_bytes = hdr1->grain_size_lbas*LBAS_SIZE; |
| |
| /* |
| * are there some bits in bitmap1[i] that are not |
| * present in bitmap2[i] ? |
| */ |
| mask = (uint8_t)(bitmap1[i] & ~bitmap2[i]); |
| if (mask) { |
| if (verbosity) { |
| fprintf(stderr, "missing bits found in byte range " |
| "[0x%"PRIx64",0x%"PRIx64"), grain size 0x%x bytes, " |
| "range diffs (gain units) %s vs %s\n", |
| diff_first_byte, diff_last_byte, grain_bytes, |
| bitmap_show_bits(bm_byte[0], bitmap1[i]), |
| bitmap_show_bits(bm_byte[1], bitmap2[i])); |
| } |
| missing_bits = 1; |
| } |
| /* |
| * are there some bits in bitmap2[i] that are not |
| * present in bitmap1[i] (extra bits)? |
| */ |
| mask = (uint8_t)(bitmap2[i] & ~bitmap1[i]); |
| if (mask) { |
| if (verbosity) { |
| fprintf(stderr, "extra bits found in byte range " |
| "[0x%"PRIx64",0x%"PRIx64"), grain size 0x%x bytes, " |
| "range diffs (grain units) %s vs %s\n", |
| diff_first_byte, diff_last_byte, grain_bytes, |
| bitmap_show_bits(bm_byte[0], bitmap1[i]), |
| bitmap_show_bits(bm_byte[1], bitmap2[i])); |
| } |
| extra_bits = 1; |
| } |
| } |
| |
| if (missing_bits) { |
| fprintf(stderr, "bitmap is missing some bits\n"); |
| return (-1); |
| } |
| |
| if (extra_bits) |
| fprintf(stderr, "bitmap has some extra bits\n"); |
| |
| /* bitmap1 is a subset of (or equal to) bitmap2 */ |
| return (0); |
| } |
| |
| uint64_t bitmap_get_bitcount(ram_bitmap_t *bm) |
| { |
| const uint64_t bitmap_size_bytes = bm->mqhdr.bitmap_size_bytes; |
| const unsigned char *bitmap = &(bm->bits[0]); |
| uint64_t i, j, bitcount; |
| |
| /* walk the bitmap and count the bits */ |
| for (bitcount = 0, i = 0; i < bitmap_size_bytes; i++) { |
| if (bitmap[i] == 0) |
| continue; |
| for (j = 0; j < 8; j++) { |
| if (bitmap[i] & (1 << j)) |
| bitcount++; |
| } |
| } |
| |
| return (bitcount); |
| } |
| |
| int bitmap_get_bit(ram_bitmap_t *bm, uint64_t byte_offset) |
| { |
| const unsigned char *bitmap = &(bm->bits[0]); |
| uint64_t grainsize = bm->mqhdr.grain_size_lbas * LBAS_SIZE; |
| uint64_t abs_bit = byte_offset / grainsize; |
| uint64_t byte = abs_bit / 8, bit = abs_bit % 8; |
| |
| if (byte >= bm->mqhdr.bitmap_size_bytes) |
| return -1; |
| |
| return ((bitmap[byte] & (1 << bit)) >> bit); |
| } |