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;

    int64_t file_length;

    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;

        }

        file_length = bdrv_getlength(bs->file->bs);

        if (file_length < 0) {

            ret = file_length;

            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 > file_length) {

            ret = -EINVAL;

            goto exit;

        }



        ret = vhdx_log_read_desc(bs, s, &logs->log, &desc_entries, true);

        if (ret < 0) {

            goto exit;

        }



        for (i = 0; i < desc_entries->hdr.descriptor_count; i++) {

            if (desc_entries->desc[i].signature == VHDX_LOG_DESC_SIGNATURE) {

                /* data sector, so read a sector to flush */

                ret = vhdx_log_read_sectors(bs, &logs->log, &sectors_read,

                                            data, 1, false);

                if (ret < 0) {

                    goto exit;

                }

                if (sectors_read != 1) {

                    ret = -EINVAL;

                    goto exit;

                }

                vhdx_log_data_le_import(data);

            }



            ret = vhdx_log_flush_desc(bs, &desc_entries->desc[i], data);

            if (ret < 0) {

                goto exit;

            }

        }

        if (file_length < 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 = QEMU_ALIGN_UP(new_file_size, MiB);

                if (new_file_size > INT64_MAX) {

                    ret = -EINVAL;

                    goto exit;

                }

                bdrv_truncate(bs->file, new_file_size, PREALLOC_MODE_OFF, NULL);

            }

        }

        qemu_vfree(desc_entries);

        desc_entries = NULL;

    }



    ret = bdrv_flush(bs);

    if (ret < 0) {

        goto exit;

    }

    /* once the log is fully flushed, indicate that we have an empty log

     * now.  This also sets the log guid to 0, to indicate an empty log */

    vhdx_log_reset(bs, s);



exit:

    qemu_vfree(data);

    qemu_vfree(desc_entries);

    return ret;

}
