func: static int vhdx_log_write(BlockDriverState *bs, BDRVVHDXState *s,
  
                            void *data, uint32_t length, uint64_t offset)
  
  {
  
      int ret = 0;
  
      void *buffer = NULL;
  
      void *merged_sector = NULL;
  
      void *data_tmp, *sector_write;
  
      unsigned int i;
  
      int sector_offset;
  
      uint32_t desc_sectors, sectors, total_length;
  
      uint32_t sectors_written = 0;
  
      uint32_t aligned_length;
  
      uint32_t leading_length = 0;
  
      uint32_t trailing_length = 0;
  
      uint32_t partial_sectors = 0;
  
      uint32_t bytes_written = 0;
  
      uint64_t file_offset;
  
      VHDXHeader *header;
  
      VHDXLogEntryHeader new_hdr;
  
      VHDXLogDescriptor *new_desc = NULL;
  
      VHDXLogDataSector *data_sector = NULL;
  
      MSGUID new_guid = { 0 };
  
  
  
      header = s->headers[s->curr_header];
  
  
  
      /* need to have offset read data, and be on 4096 byte boundary */
  
  
  
      if (length > header->log_length) {
  
          /* no log present.  we could create a log here instead of failing */
  
          ret = -EINVAL;
  
          goto exit;
  
      }
  
  
  
      if (guid_eq(header->log_guid, zero_guid)) {
  
          vhdx_guid_generate(&new_guid);
  
          vhdx_update_headers(bs, s, false, &new_guid);
  
      } else {
  
          /* currently, we require that the log be flushed after
  
           * every write. */
  
          ret = -ENOTSUP;
  
          goto exit;
  
      }
  
  
  
      /* 0 is an invalid sequence number, but may also represent the first
  
       * log write (or a wrapped seq) */
  
      if (s->log.sequence == 0) {
  
          s->log.sequence = 1;
  
      }
  
  
  
      sector_offset = offset % VHDX_LOG_SECTOR_SIZE;
  
      file_offset = (offset / VHDX_LOG_SECTOR_SIZE) * VHDX_LOG_SECTOR_SIZE;
  
  
  
      aligned_length = length;
  
  
  
      /* add in the unaligned head and tail bytes */
  
      if (sector_offset) {
  
          leading_length = (VHDX_LOG_SECTOR_SIZE - sector_offset);
  
          leading_length = leading_length > length ? length : leading_length;
  
          aligned_length -= leading_length;
  
          partial_sectors++;
  
      }
  
  
  
      sectors = aligned_length / VHDX_LOG_SECTOR_SIZE;
  
      trailing_length = aligned_length - (sectors * VHDX_LOG_SECTOR_SIZE);
  
      if (trailing_length) {
  
          partial_sectors++;
  
      }
  
  
  
      sectors += partial_sectors;
  
  
  
      /* sectors is now how many sectors the data itself takes, not
  
       * including the header and descriptor metadata */
  
  
  
      new_hdr = (VHDXLogEntryHeader) {
  
                  .signature           = VHDX_LOG_SIGNATURE,
  
                  .tail                = s->log.tail,
  
                  .sequence_number     = s->log.sequence,
  
                  .descriptor_count    = sectors,
  
                  .reserved            = 0,
  
                  .flushed_file_offset = bdrv_getlength(bs->file->bs),
  
                  .last_file_offset    = bdrv_getlength(bs->file->bs),
  
                };
  
  
  
      new_hdr.log_guid = header->log_guid;
  
  
  
      desc_sectors = vhdx_compute_desc_sectors(new_hdr.descriptor_count);
  
  
  
      total_length = (desc_sectors + sectors) * VHDX_LOG_SECTOR_SIZE;
  
      new_hdr.entry_length = total_length;
  
  
  
      vhdx_log_entry_hdr_le_export(&new_hdr);
  
  
  
      buffer = qemu_blockalign(bs, total_length);
  
      memcpy(buffer, &new_hdr, sizeof(new_hdr));
  
  
  
      new_desc = buffer + sizeof(new_hdr);
  
      data_sector = buffer + (desc_sectors * VHDX_LOG_SECTOR_SIZE);
  
      data_tmp = data;
  
  
  
      /* All log sectors are 4KB, so for any partial sectors we must
  
       * merge the data with preexisting data from the final file
  
       * destination */
  
      merged_sector = qemu_blockalign(bs, VHDX_LOG_SECTOR_SIZE);
  
  
  
      for (i = 0; i < sectors; i++) {
  
          new_desc->signature       = VHDX_LOG_DESC_SIGNATURE;
  
          new_desc->sequence_number = s->log.sequence;
  
          new_desc->file_offset     = file_offset;
  
  
  
          if (i == 0 && leading_length) {
  
              /* partial sector at the front of the buffer */
  
              ret = bdrv_pread(bs->file, file_offset, merged_sector,
  
                               VHDX_LOG_SECTOR_SIZE);
  
              if (ret < 0) {
  
                  goto exit;
  
              }
  
              memcpy(merged_sector + sector_offset, data_tmp, leading_length);
  
              bytes_written = leading_length;
  
              sector_write = merged_sector;
  
          } else if (i == sectors - 1 && trailing_length) {
  
              /* partial sector at the end of the buffer */
  
              ret = bdrv_pread(bs->file,
  
                              file_offset,
  
                              merged_sector + trailing_length,
  
                              VHDX_LOG_SECTOR_SIZE - trailing_length);
  
              if (ret < 0) {
  
                  goto exit;
  
              }
  
              memcpy(merged_sector, data_tmp, trailing_length);
  
              bytes_written = trailing_length;
  
              sector_write = merged_sector;
  
          } else {
  
              bytes_written = VHDX_LOG_SECTOR_SIZE;
  
              sector_write = data_tmp;
  
          }
  
  
  
          /* populate the raw sector data into the proper structures,
  
           * as well as update the descriptor, and convert to proper
  
           * endianness */
  
          vhdx_log_raw_to_le_sector(new_desc, data_sector, sector_write,
  
                                    s->log.sequence);
  
  
  
          data_tmp += bytes_written;
  
          data_sector++;
  
          new_desc++;
  
          file_offset += VHDX_LOG_SECTOR_SIZE;
  
      }
  
  
  
      /* checksum covers entire entry, from the log header through the
  
       * last data sector */
  
      vhdx_update_checksum(buffer, total_length,
  
                           offsetof(VHDXLogEntryHeader, checksum));
  
  
  
      /* now write to the log */
  
      ret = vhdx_log_write_sectors(bs, &s->log, &sectors_written, buffer,
  
                                   desc_sectors + sectors);
  
      if (ret < 0) {
  
          goto exit;
  
      }
  
  
  
      if (sectors_written != desc_sectors + sectors) {
  
          /* instead of failing, we could flush the log here */
  
          ret = -EINVAL;
  
          goto exit;
  
      }
  
  
  
      s->log.sequence++;
  
      /* write new tail */
  
      s->log.tail = s->log.write;
  
  
  
  exit:
  
      qemu_vfree(buffer);
  
      qemu_vfree(merged_sector);
  
      return ret;
  
  }
  