/* Writes num_sectors to the log (all log sectors are 4096 bytes), * from buffer 'buffer'. Upon return, *sectors_written will contain * the number of sectors successfully written. * * It is assumed that 'buffer' is at least 4096*num_sectors large. * * 0 is returned on success, -errno otherwise */ static int vhdx_log_write_sectors(BlockDriverState *bs, VHDXLogEntries *log, uint32_t *sectors_written, void *buffer, uint32_t num_sectors) { int ret = 0; uint64_t offset; uint32_t write; void *buffer_tmp; BDRVVHDXState *s = bs->opaque; ret = vhdx_user_visible_write(bs, s); if (ret < 0) { goto exit; } write = log->write; buffer_tmp = buffer; while (num_sectors) { offset = log->offset + write; write = vhdx_log_inc_idx(write, log->length); if (write == log->read) { /* full */ break; } ret = bdrv_pwrite(bs->file->bs, offset, buffer_tmp, VHDX_LOG_SECTOR_SIZE); if (ret < 0) { goto exit; } buffer_tmp += VHDX_LOG_SECTOR_SIZE; log->write = write; *sectors_written = *sectors_written + 1; num_sectors--; } exit: return ret; }
/* Flush the entire log (as described by 'logs') to the VHDX image * file, and then set the log to 'empty' status once complete. * * The log entries should be validate prior to flushing */ static int vhdx_log_flush(BlockDriverState *bs, BDRVVHDXState *s, VHDXLogSequence *logs) { int ret = 0; int i; uint32_t cnt, sectors_read; uint64_t new_file_size; void *data = NULL; VHDXLogDescEntries *desc_entries = NULL; VHDXLogEntryHeader hdr_tmp = { 0 }; cnt = logs->count; data = qemu_blockalign(bs, VHDX_LOG_SECTOR_SIZE); ret = vhdx_user_visible_write(bs, s); if (ret < 0) { goto exit; } /* each iteration represents one log sequence, which may span multiple * sectors */ while (cnt--) { ret = vhdx_log_peek_hdr(bs, &logs->log, &hdr_tmp); if (ret < 0) { goto exit; } /* if the log shows a FlushedFileOffset larger than our current file * size, then that means the file has been truncated / corrupted, and * we must refused to open it / use it */ if (hdr_tmp.flushed_file_offset > bdrv_getlength(bs->file)) { ret = -EINVAL; goto exit; } ret = vhdx_log_read_desc(bs, s, &logs->log, &desc_entries); if (ret < 0) { goto exit; } for (i = 0; i < desc_entries->hdr.descriptor_count; i++) { if (!memcmp(&desc_entries->desc[i].signature, "desc", 4)) { /* data sector, so read a sector to flush */ ret = vhdx_log_read_sectors(bs, &logs->log, §ors_read, data, 1, false); if (ret < 0) { goto exit; } if (sectors_read != 1) { ret = -EINVAL; goto exit; } } ret = vhdx_log_flush_desc(bs, &desc_entries->desc[i], data); if (ret < 0) { goto exit; } } if (bdrv_getlength(bs->file) < desc_entries->hdr.last_file_offset) { new_file_size = desc_entries->hdr.last_file_offset; if (new_file_size % (1024*1024)) { /* round up to nearest 1MB boundary */ new_file_size = ((new_file_size >> 20) + 1) << 20; bdrv_truncate(bs->file, new_file_size); } }