static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,

                                                   int64_t offset,

                                                   int64_t length,

                                                   uint64_t addend,

                                                   bool decrease,

                                                   enum qcow2_discard_type type)

{

    BDRVQcowState *s = bs->opaque;

    int64_t start, last, cluster_offset;

    void *refcount_block = NULL;

    int64_t old_table_index = -1;

    int ret;



#ifdef DEBUG_ALLOC2

    fprintf(stderr, "update_refcount: offset=%" PRId64 " size=%" PRId64

            " addend=%s%" PRIu64 "\n", offset, length, decrease ? "-" : "",

            addend);

#endif

    if (length < 0) {

        return -EINVAL;

    } else if (length == 0) {

        return 0;

    }



    if (decrease) {

        qcow2_cache_set_dependency(bs, s->refcount_block_cache,

            s->l2_table_cache);

    }



    start = start_of_cluster(s, offset);

    last = start_of_cluster(s, offset + length - 1);

    for(cluster_offset = start; cluster_offset <= last;

        cluster_offset += s->cluster_size)

    {

        int block_index;

        uint64_t refcount;

        int64_t cluster_index = cluster_offset >> s->cluster_bits;

        int64_t table_index = cluster_index >> s->refcount_block_bits;



        /* Load the refcount block and allocate it if needed */

        if (table_index != old_table_index) {

            if (refcount_block) {

                ret = qcow2_cache_put(bs, s->refcount_block_cache,

                                      &refcount_block);

                if (ret < 0) {

                    goto fail;

                }

            }



            ret = alloc_refcount_block(bs, cluster_index, &refcount_block);

            if (ret < 0) {

                goto fail;

            }

        }

        old_table_index = table_index;



        qcow2_cache_entry_mark_dirty(bs, s->refcount_block_cache,

                                     refcount_block);



        /* we can update the count and save it */

        block_index = cluster_index & (s->refcount_block_size - 1);



        refcount = s->get_refcount(refcount_block, block_index);

        if (decrease ? (refcount - addend > refcount)

                     : (refcount + addend < refcount ||

                        refcount + addend > s->refcount_max))

        {

            ret = -EINVAL;

            goto fail;

        }

        if (decrease) {

            refcount -= addend;

        } else {

            refcount += addend;

        }

        if (refcount == 0 && cluster_index < s->free_cluster_index) {

            s->free_cluster_index = cluster_index;

        }

        s->set_refcount(refcount_block, block_index, refcount);



        if (refcount == 0 && s->discard_passthrough[type]) {

            update_refcount_discard(bs, cluster_offset, s->cluster_size);

        }

    }



    ret = 0;

fail:

    if (!s->cache_discards) {

        qcow2_process_discards(bs, ret);

    }



    /* Write last changed block to disk */

    if (refcount_block) {

        int wret;

        wret = qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block);

        if (wret < 0) {

            return ret < 0 ? ret : wret;

        }

    }



    /*

     * Try do undo any updates if an error is returned (This may succeed in

     * some cases like ENOSPC for allocating a new refcount block)

     */

    if (ret < 0) {

        int dummy;

        dummy = update_refcount(bs, offset, cluster_offset - offset, addend,

                                !decrease, QCOW2_DISCARD_NEVER);

        (void)dummy;

    }



    return ret;

}
