Copy data when writing to the device. This patch fixes damaged blocks on encrypted device when the computer crashes. Linux IO architecture allows the data to be modified while they are being written. While we are processing write requests, the data may change, and it is expected, that each byte written to the device is either old byte or new byte. Some crypto functions may read the input buffer more than once and so they'll produce garbage if the buffer changes under them. When this garbage is read again and decrypted, it may produce damage in all bytes in the block. This damage is visible only if the computer crashes; if it didn't crash, the memory manager writes correct buffer or page contents some times later. So we first copy data to a temporary buffer with memcpy and then encrypt them in place. Signed-off-by: Mikulas Patocka --- drivers/md/dm-crypt.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) Index: linux-2.6.25.3/drivers/md/dm-crypt.c =================================================================== --- linux-2.6.25.3.orig/drivers/md/dm-crypt.c 2008-05-23 21:04:40.000000000 +0200 +++ linux-2.6.25.3/drivers/md/dm-crypt.c 2008-05-23 21:04:49.000000000 +0200 @@ -361,8 +361,18 @@ static int crypt_convert_block(struct cr crypto_ablkcipher_alignmask(cc->tfm) + 1); sg_init_table(&dmreq->sg_in, 1); - sg_set_page(&dmreq->sg_in, bv_in->bv_page, 1 << SECTOR_SHIFT, - bv_in->bv_offset + ctx->offset_in); + if (bio_data_dir(ctx->bio_in) == WRITE) { + char *page_in = kmap_atomic(bv_in->bv_page, KM_USER0); + char *page_out = kmap_atomic(bv_out->bv_page, KM_USER1); + memcpy(page_out + bv_out->bv_offset + ctx->offset_out, page_in + bv_in->bv_offset + ctx->offset_in, 1 << SECTOR_SHIFT); + kunmap_atomic(page_in, KM_USER0); + kunmap_atomic(page_out, KM_USER1); + + sg_set_page(&dmreq->sg_in, bv_out->bv_page, 1 << SECTOR_SHIFT, + bv_out->bv_offset + ctx->offset_out); + } else + sg_set_page(&dmreq->sg_in, bv_in->bv_page, 1 << SECTOR_SHIFT, + bv_in->bv_offset + ctx->offset_in); sg_init_table(&dmreq->sg_out, 1); sg_set_page(&dmreq->sg_out, bv_out->bv_page, 1 << SECTOR_SHIFT,