static void sdev_free(struct device *dev) { struct safe_device *sdev = dev_sdev(dev); if (sdev->sb_n > 0) { char *first_block = align_512(sdev->saved_blocks); char *block = first_block + ((sdev->sb_n - 1) << dev_get_block_order(sdev->shadow_dev)); uint64_t *poffset = &sdev->sb_offsets[sdev->sb_n - 1]; int block_size = dev_get_block_size(sdev->shadow_dev); /* Restore blocks in reverse order to cope with * wraparound and chain drives. */ do { int rc = sdev->shadow_dev->write_block( sdev->shadow_dev, block, block_size, *poffset); if (rc) { /* Do not abort, try to recover all bocks. */ warn("Failed to recover block at offset 0x%" PRIx64 " due to a write error", *poffset); } block -= block_size; poffset--; } while (block >= first_block); } free(sdev->sb_bitmap); free(sdev->sb_offsets); free(sdev->saved_blocks); free_device(sdev->shadow_dev); }
/* Return true if @b1 and b2 are at most @tolerance_byte bytes different. */ static int similar_blk(struct device *dev, const char *b1, const char *b2, int tolerance_byte) { const int block_size = dev_get_block_size(dev); int i; for (i = 0; i < block_size; i++) { if (*b1 != *b2) { tolerance_byte--; if (tolerance_byte <= 0) return false; } b1++; b2++; } return true; }
static int sdev_save_block(struct safe_device *sdev, int length, uint64_t offset) { const int block_order = dev_get_block_order(sdev->shadow_dev); lldiv_t idx = lldiv(offset >> block_order, SDEV_BITMAP_BITS_PER_WORD); SDEV_BITMAP_WORD set_bit = (SDEV_BITMAP_WORD)1 << idx.rem; char *block; int rc; /* The current implementation doesn't support variable lengths. */ assert(length == dev_get_block_size(sdev->shadow_dev)); /* Is this block already saved? */ if (!sdev->sb_bitmap) { int i; /* Running without bitmap. */ for (i = 0; i < sdev->sb_n; i++) if (sdev->sb_offsets[i] == offset) { /* The block at @offset is already saved. */ return 0; } } else if (sdev->sb_bitmap[idx.quot] & set_bit) { /* The block at @offset is already saved. */ return 0; } /* The block at @offset hasn't been saved before. Save this block. */ assert(sdev->sb_n < sdev->sb_max); block = (char *)align_512(sdev->saved_blocks) + (sdev->sb_n << block_order); rc = sdev->shadow_dev->read_block(sdev->shadow_dev, block, length, offset); if (rc) return rc; /* Bookkeeping. */ if (sdev->sb_bitmap) sdev->sb_bitmap[idx.quot] |= set_bit; sdev->sb_offsets[sdev->sb_n] = offset; sdev->sb_n++; return 0; }
static inline int equal_blk(struct device *dev, const char *b1, const char *b2) { return !memcmp(b1, b2, dev_get_block_size(dev)); }