func: static int rebuild_refcount_structure(BlockDriverState *bs,
  
                                        BdrvCheckResult *res,
  
                                        void **refcount_table,
  
                                        int64_t *nb_clusters)
  
  {
  
      BDRVQcow2State *s = bs->opaque;
  
      int64_t first_free_cluster = 0, reftable_offset = -1, cluster = 0;
  
      int64_t refblock_offset, refblock_start, refblock_index;
  
      uint32_t reftable_size = 0;
  
      uint64_t *on_disk_reftable = NULL;
  
      void *on_disk_refblock;
  
      int ret = 0;
  
      struct {
  
          uint64_t reftable_offset;
  
          uint32_t reftable_clusters;
  
      } QEMU_PACKED reftable_offset_and_clusters;
  
  
  
      qcow2_cache_empty(bs, s->refcount_block_cache);
  
  
  
  write_refblocks:
  
      for (; cluster < *nb_clusters; cluster++) {
  
          if (!s->get_refcount(*refcount_table, cluster)) {
  
              continue;
  
          }
  
  
  
          refblock_index = cluster >> s->refcount_block_bits;
  
          refblock_start = refblock_index << s->refcount_block_bits;
  
  
  
          /* Don't allocate a cluster in a refblock already written to disk */
  
          if (first_free_cluster < refblock_start) {
  
              first_free_cluster = refblock_start;
  
          }
  
          refblock_offset = alloc_clusters_imrt(bs, 1, refcount_table,
  
                                                nb_clusters, &first_free_cluster);
  
          if (refblock_offset < 0) {
  
              fprintf(stderr, "ERROR allocating refblock: %s\n",
  
                      strerror(-refblock_offset));
  
              res->check_errors++;
  
              ret = refblock_offset;
  
              goto fail;
  
          }
  
  
  
          if (reftable_size <= refblock_index) {
  
              uint32_t old_reftable_size = reftable_size;
  
              uint64_t *new_on_disk_reftable;
  
  
  
              reftable_size = ROUND_UP((refblock_index + 1) * sizeof(uint64_t),
  
                                       s->cluster_size) / sizeof(uint64_t);
  
              new_on_disk_reftable = g_try_realloc(on_disk_reftable,
  
                                                   reftable_size *
  
                                                   sizeof(uint64_t));
  
              if (!new_on_disk_reftable) {
  
                  res->check_errors++;
  
                  ret = -ENOMEM;
  
                  goto fail;
  
              }
  
              on_disk_reftable = new_on_disk_reftable;
  
  
  
              memset(on_disk_reftable + old_reftable_size, 0,
  
                     (reftable_size - old_reftable_size) * sizeof(uint64_t));
  
  
  
              /* The offset we have for the reftable is now no longer valid;
  
               * this will leak that range, but we can easily fix that by running
  
               * a leak-fixing check after this rebuild operation */
  
              reftable_offset = -1;
  
          }
  
          on_disk_reftable[refblock_index] = refblock_offset;
  
  
  
          /* If this is apparently the last refblock (for now), try to squeeze the
  
           * reftable in */
  
          if (refblock_index == (*nb_clusters - 1) >> s->refcount_block_bits &&
  
              reftable_offset < 0)
  
          {
  
              uint64_t reftable_clusters = size_to_clusters(s, reftable_size *
  
                                                            sizeof(uint64_t));
  
              reftable_offset = alloc_clusters_imrt(bs, reftable_clusters,
  
                                                    refcount_table, nb_clusters,
  
                                                    &first_free_cluster);
  
              if (reftable_offset < 0) {
  
                  fprintf(stderr, "ERROR allocating reftable: %s\n",
  
                          strerror(-reftable_offset));
  
                  res->check_errors++;
  
                  ret = reftable_offset;
  
                  goto fail;
  
              }
  
          }
  
  
  
          ret = qcow2_pre_write_overlap_check(bs, 0, refblock_offset,
  
                                              s->cluster_size);
  
          if (ret < 0) {
  
              fprintf(stderr, "ERROR writing refblock: %s\n", strerror(-ret));
  
              goto fail;
  
          }
  
  
  
          /* The size of *refcount_table is always cluster-aligned, therefore the
  
           * write operation will not overflow */
  
          on_disk_refblock = (void *)((char *) *refcount_table +
  
                                      refblock_index * s->cluster_size);
  
  
  
          ret = bdrv_write(bs->file, refblock_offset / BDRV_SECTOR_SIZE,
  
                           on_disk_refblock, s->cluster_sectors);
  
          if (ret < 0) {
  
              fprintf(stderr, "ERROR writing refblock: %s\n", strerror(-ret));
  
              goto fail;
  
          }
  
  
  
          /* Go to the end of this refblock */
  
          cluster = refblock_start + s->refcount_block_size - 1;
  
      }
  
  
  
      if (reftable_offset < 0) {
  
          uint64_t post_refblock_start, reftable_clusters;
  
  
  
          post_refblock_start = ROUND_UP(*nb_clusters, s->refcount_block_size);
  
          reftable_clusters = size_to_clusters(s,
  
                                               reftable_size * sizeof(uint64_t));
  
          /* Not pretty but simple */
  
          if (first_free_cluster < post_refblock_start) {
  
              first_free_cluster = post_refblock_start;
  
          }
  
          reftable_offset = alloc_clusters_imrt(bs, reftable_clusters,
  
                                                refcount_table, nb_clusters,
  
                                                &first_free_cluster);
  
          if (reftable_offset < 0) {
  
              fprintf(stderr, "ERROR allocating reftable: %s\n",
  
                      strerror(-reftable_offset));
  
              res->check_errors++;
  
              ret = reftable_offset;
  
              goto fail;
  
          }
  
  
  
          goto write_refblocks;
  
      }
  
  
  
      assert(on_disk_reftable);
  
  
  
      for (refblock_index = 0; refblock_index < reftable_size; refblock_index++) {
  
          cpu_to_be64s(&on_disk_reftable[refblock_index]);
  
      }
  
  
  
      ret = qcow2_pre_write_overlap_check(bs, 0, reftable_offset,
  
                                          reftable_size * sizeof(uint64_t));
  
      if (ret < 0) {
  
          fprintf(stderr, "ERROR writing reftable: %s\n", strerror(-ret));
  
          goto fail;
  
      }
  
  
  
      assert(reftable_size < INT_MAX / sizeof(uint64_t));
  
      ret = bdrv_pwrite(bs->file, reftable_offset, on_disk_reftable,
  
                        reftable_size * sizeof(uint64_t));
  
      if (ret < 0) {
  
          fprintf(stderr, "ERROR writing reftable: %s\n", strerror(-ret));
  
          goto fail;
  
      }
  
  
  
      /* Enter new reftable into the image header */
  
      reftable_offset_and_clusters.reftable_offset = cpu_to_be64(reftable_offset);
  
      reftable_offset_and_clusters.reftable_clusters =
  
          cpu_to_be32(size_to_clusters(s, reftable_size * sizeof(uint64_t)));
  
      ret = bdrv_pwrite_sync(bs->file,
  
                             offsetof(QCowHeader, refcount_table_offset),
  
                             &reftable_offset_and_clusters,
  
                             sizeof(reftable_offset_and_clusters));
  
      if (ret < 0) {
  
          fprintf(stderr, "ERROR setting reftable: %s\n", strerror(-ret));
  
          goto fail;
  
      }
  
  
  
      for (refblock_index = 0; refblock_index < reftable_size; refblock_index++) {
  
          be64_to_cpus(&on_disk_reftable[refblock_index]);
  
      }
  
      s->refcount_table = on_disk_reftable;
  
      s->refcount_table_offset = reftable_offset;
  
      s->refcount_table_size = reftable_size;
  
      update_max_refcount_table_index(s);
  
  
  
      return 0;
  
  
  
  fail:
  
      g_free(on_disk_reftable);
  
      return ret;
  
  }
  