int qcow2_update_snapshot_refcount(BlockDriverState *bs,

    int64_t l1_table_offset, int l1_size, int addend)

{

    BDRVQcow2State *s = bs->opaque;

    uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, refcount;

    bool l1_allocated = false;

    int64_t old_offset, old_l2_offset;

    int i, j, l1_modified = 0, nb_csectors;

    int ret;



    assert(addend >= -1 && addend <= 1);



    l2_table = NULL;

    l1_table = NULL;

    l1_size2 = l1_size * sizeof(uint64_t);



    s->cache_discards = true;



    /* WARNING: qcow2_snapshot_goto relies on this function not using the

     * l1_table_offset when it is the current s->l1_table_offset! Be careful

     * when changing this! */

    if (l1_table_offset != s->l1_table_offset) {

        l1_table = g_try_malloc0(align_offset(l1_size2, 512));

        if (l1_size2 && l1_table == NULL) {

            ret = -ENOMEM;

            goto fail;

        }

        l1_allocated = true;



        ret = bdrv_pread(bs->file, l1_table_offset, l1_table, l1_size2);

        if (ret < 0) {

            goto fail;

        }



        for(i = 0;i < l1_size; i++)

            be64_to_cpus(&l1_table[i]);

    } else {

        assert(l1_size == s->l1_size);

        l1_table = s->l1_table;

        l1_allocated = false;

    }



    for(i = 0; i < l1_size; i++) {

        l2_offset = l1_table[i];

        if (l2_offset) {

            old_l2_offset = l2_offset;

            l2_offset &= L1E_OFFSET_MASK;



            if (offset_into_cluster(s, l2_offset)) {

                qcow2_signal_corruption(bs, true, -1, -1, "L2 table offset %#"

                                        PRIx64 " unaligned (L1 index: %#x)",

                                        l2_offset, i);

                ret = -EIO;

                goto fail;

            }



            ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset,

                (void**) &l2_table);

            if (ret < 0) {

                goto fail;

            }



            for(j = 0; j < s->l2_size; j++) {

                uint64_t cluster_index;



                offset = be64_to_cpu(l2_table[j]);

                old_offset = offset;

                offset &= ~QCOW_OFLAG_COPIED;



                switch (qcow2_get_cluster_type(offset)) {

                    case QCOW2_CLUSTER_COMPRESSED:

                        nb_csectors = ((offset >> s->csize_shift) &

                                       s->csize_mask) + 1;

                        if (addend != 0) {

                            ret = update_refcount(bs,

                                (offset & s->cluster_offset_mask) & ~511,

                                nb_csectors * 512, abs(addend), addend < 0,

                                QCOW2_DISCARD_SNAPSHOT);

                            if (ret < 0) {

                                goto fail;

                            }

                        }

                        /* compressed clusters are never modified */

                        refcount = 2;

                        break;



                    case QCOW2_CLUSTER_NORMAL:

                    case QCOW2_CLUSTER_ZERO:

                        if (offset_into_cluster(s, offset & L2E_OFFSET_MASK)) {

                            qcow2_signal_corruption(bs, true, -1, -1, "Data "

                                                    "cluster offset %#llx "

                                                    "unaligned (L2 offset: %#"

                                                    PRIx64 ", L2 index: %#x)",

                                                    offset & L2E_OFFSET_MASK,

                                                    l2_offset, j);

                            ret = -EIO;

                            goto fail;

                        }



                        cluster_index = (offset & L2E_OFFSET_MASK) >> s->cluster_bits;

                        if (!cluster_index) {

                            /* unallocated */

                            refcount = 0;

                            break;

                        }

                        if (addend != 0) {

                            ret = qcow2_update_cluster_refcount(bs,

                                    cluster_index, abs(addend), addend < 0,

                                    QCOW2_DISCARD_SNAPSHOT);

                            if (ret < 0) {

                                goto fail;

                            }

                        }



                        ret = qcow2_get_refcount(bs, cluster_index, &refcount);

                        if (ret < 0) {

                            goto fail;

                        }

                        break;



                    case QCOW2_CLUSTER_UNALLOCATED:

                        refcount = 0;

                        break;



                    default:

                        abort();

                }



                if (refcount == 1) {

                    offset |= QCOW_OFLAG_COPIED;

                }

                if (offset != old_offset) {

                    if (addend > 0) {

                        qcow2_cache_set_dependency(bs, s->l2_table_cache,

                            s->refcount_block_cache);

                    }

                    l2_table[j] = cpu_to_be64(offset);

                    qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache,

                                                 l2_table);

                }

            }



            qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table);



            if (addend != 0) {

                ret = qcow2_update_cluster_refcount(bs, l2_offset >>

                                                        s->cluster_bits,

                                                    abs(addend), addend < 0,

                                                    QCOW2_DISCARD_SNAPSHOT);

                if (ret < 0) {

                    goto fail;

                }

            }

            ret = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits,

                                     &refcount);

            if (ret < 0) {

                goto fail;

            } else if (refcount == 1) {

                l2_offset |= QCOW_OFLAG_COPIED;

            }

            if (l2_offset != old_l2_offset) {

                l1_table[i] = l2_offset;

                l1_modified = 1;

            }

        }

    }



    ret = bdrv_flush(bs);

fail:

    if (l2_table) {

        qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);

    }



    s->cache_discards = false;

    qcow2_process_discards(bs, ret);



    /* Update L1 only if it isn't deleted anyway (addend = -1) */

    if (ret == 0 && addend >= 0 && l1_modified) {

        for (i = 0; i < l1_size; i++) {

            cpu_to_be64s(&l1_table[i]);

        }



        ret = bdrv_pwrite_sync(bs->file, l1_table_offset,

                               l1_table, l1_size2);



        for (i = 0; i < l1_size; i++) {

            be64_to_cpus(&l1_table[i]);

        }

    }

    if (l1_allocated)

        g_free(l1_table);

    return ret;

}

int qcow2_update_snapshot_refcount(BlockDriverState *bs,

    int64_t l1_table_offset, int l1_size, int addend)

{

    BDRVQcow2State *s = bs->opaque;

    uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, refcount;

    bool l1_allocated = false;

    int64_t old_offset, old_l2_offset;

    int i, j, l1_modified = 0, nb_csectors;

    int ret;



    assert(addend >= -1 && addend <= 1);



    l2_table = NULL;

    l1_table = NULL;

    l1_size2 = l1_size * sizeof(uint64_t);



    s->cache_discards = true;



    





    if (l1_table_offset != s->l1_table_offset) {

        l1_table = g_try_malloc0(align_offset(l1_size2, 512));

        if (l1_size2 && l1_table == NULL) {

            ret = -ENOMEM;

            goto fail;

        }

        l1_allocated = true;



        ret = bdrv_pread(bs->file, l1_table_offset, l1_table, l1_size2);

        if (ret < 0) {

            goto fail;

        }



        for(i = 0;i < l1_size; i++)

            be64_to_cpus(&l1_table[i]);

    } else {

        assert(l1_size == s->l1_size);

        l1_table = s->l1_table;

        l1_allocated = false;

    }



    for(i = 0; i < l1_size; i++) {

        l2_offset = l1_table[i];

        if (l2_offset) {

            old_l2_offset = l2_offset;

            l2_offset &= L1E_OFFSET_MASK;



            if (offset_into_cluster(s, l2_offset)) {

                qcow2_signal_corruption(bs, true, -1, -1, "L2 table offset %#"

                                        PRIx64 " unaligned (L1 index: %#x)",

                                        l2_offset, i);

                ret = -EIO;

                goto fail;

            }



            ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset,

                (void**) &l2_table);

            if (ret < 0) {

                goto fail;

            }



            for(j = 0; j < s->l2_size; j++) {

                uint64_t cluster_index;



                offset = be64_to_cpu(l2_table[j]);

                old_offset = offset;

                offset &= ~QCOW_OFLAG_COPIED;



                switch (qcow2_get_cluster_type(offset)) {

                    case QCOW2_CLUSTER_COMPRESSED:

                        nb_csectors = ((offset >> s->csize_shift) &

                                       s->csize_mask) + 1;

                        if (addend != 0) {

                            ret = update_refcount(bs,

                                (offset & s->cluster_offset_mask) & ~511,

                                nb_csectors * 512, abs(addend), addend < 0,

                                QCOW2_DISCARD_SNAPSHOT);

                            if (ret < 0) {

                                goto fail;

                            }

                        }

                        

                        refcount = 2;

                        break;



                    case QCOW2_CLUSTER_NORMAL:

                    case QCOW2_CLUSTER_ZERO:

                        if (offset_into_cluster(s, offset & L2E_OFFSET_MASK)) {

                            qcow2_signal_corruption(bs, true, -1, -1, "Data "

                                                    "cluster offset %#llx "

                                                    "unaligned (L2 offset: %#"

                                                    PRIx64 ", L2 index: %#x)",

                                                    offset & L2E_OFFSET_MASK,

                                                    l2_offset, j);

                            ret = -EIO;

                            goto fail;

                        }



                        cluster_index = (offset & L2E_OFFSET_MASK) >> s->cluster_bits;

                        if (!cluster_index) {

                            

                            refcount = 0;

                            break;

                        }

                        if (addend != 0) {

                            ret = qcow2_update_cluster_refcount(bs,

                                    cluster_index, abs(addend), addend < 0,

                                    QCOW2_DISCARD_SNAPSHOT);

                            if (ret < 0) {

                                goto fail;

                            }

                        }



                        ret = qcow2_get_refcount(bs, cluster_index, &refcount);

                        if (ret < 0) {

                            goto fail;

                        }

                        break;



                    case QCOW2_CLUSTER_UNALLOCATED:

                        refcount = 0;

                        break;



                    default:

                        abort();

                }



                if (refcount == 1) {

                    offset |= QCOW_OFLAG_COPIED;

                }

                if (offset != old_offset) {

                    if (addend > 0) {

                        qcow2_cache_set_dependency(bs, s->l2_table_cache,

                            s->refcount_block_cache);

                    }

                    l2_table[j] = cpu_to_be64(offset);

                    qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache,

                                                 l2_table);

                }

            }



            qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table);



            if (addend != 0) {

                ret = qcow2_update_cluster_refcount(bs, l2_offset >>

                                                        s->cluster_bits,

                                                    abs(addend), addend < 0,

                                                    QCOW2_DISCARD_SNAPSHOT);

                if (ret < 0) {

                    goto fail;

                }

            }

            ret = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits,

                                     &refcount);

            if (ret < 0) {

                goto fail;

            } else if (refcount == 1) {

                l2_offset |= QCOW_OFLAG_COPIED;

            }

            if (l2_offset != old_l2_offset) {

                l1_table[i] = l2_offset;

                l1_modified = 1;

            }

        }

    }



    ret = bdrv_flush(bs);

fail:

    if (l2_table) {

        qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);

    }



    s->cache_discards = false;

    qcow2_process_discards(bs, ret);



    

    if (ret == 0 && addend >= 0 && l1_modified) {

        for (i = 0; i < l1_size; i++) {

            cpu_to_be64s(&l1_table[i]);

        }



        ret = bdrv_pwrite_sync(bs->file, l1_table_offset,

                               l1_table, l1_size2);



        for (i = 0; i < l1_size; i++) {

            be64_to_cpus(&l1_table[i]);

        }

    }

    if (l1_allocated)

        g_free(l1_table);

    return ret;

}

int FUNC_0(BlockDriverState *VAR_0,

    int64_t VAR_1, int VAR_2, int VAR_3)

{

    BDRVQcow2State *s = VAR_0->opaque;

    uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, refcount;

    bool l1_allocated = false;

    int64_t old_offset, old_l2_offset;

    int VAR_4, VAR_5, VAR_6 = 0, VAR_7;

    int VAR_8;



    assert(VAR_3 >= -1 && VAR_3 <= 1);



    l2_table = NULL;

    l1_table = NULL;

    l1_size2 = VAR_2 * sizeof(uint64_t);



    s->cache_discards = true;



    





    if (VAR_1 != s->VAR_1) {

        l1_table = g_try_malloc0(align_offset(l1_size2, 512));

        if (l1_size2 && l1_table == NULL) {

            VAR_8 = -ENOMEM;

            goto fail;

        }

        l1_allocated = true;



        VAR_8 = bdrv_pread(VAR_0->file, VAR_1, l1_table, l1_size2);

        if (VAR_8 < 0) {

            goto fail;

        }



        for(VAR_4 = 0;VAR_4 < VAR_2; VAR_4++)

            be64_to_cpus(&l1_table[VAR_4]);

    } else {

        assert(VAR_2 == s->VAR_2);

        l1_table = s->l1_table;

        l1_allocated = false;

    }



    for(VAR_4 = 0; VAR_4 < VAR_2; VAR_4++) {

        l2_offset = l1_table[VAR_4];

        if (l2_offset) {

            old_l2_offset = l2_offset;

            l2_offset &= L1E_OFFSET_MASK;



            if (offset_into_cluster(s, l2_offset)) {

                qcow2_signal_corruption(VAR_0, true, -1, -1, "L2 table offset %#"

                                        PRIx64 " unaligned (L1 index: %#x)",

                                        l2_offset, VAR_4);

                VAR_8 = -EIO;

                goto fail;

            }



            VAR_8 = qcow2_cache_get(VAR_0, s->l2_table_cache, l2_offset,

                (void**) &l2_table);

            if (VAR_8 < 0) {

                goto fail;

            }



            for(VAR_5 = 0; VAR_5 < s->l2_size; VAR_5++) {

                uint64_t cluster_index;



                offset = be64_to_cpu(l2_table[VAR_5]);

                old_offset = offset;

                offset &= ~QCOW_OFLAG_COPIED;



                switch (qcow2_get_cluster_type(offset)) {

                    case QCOW2_CLUSTER_COMPRESSED:

                        VAR_7 = ((offset >> s->csize_shift) &

                                       s->csize_mask) + 1;

                        if (VAR_3 != 0) {

                            VAR_8 = update_refcount(VAR_0,

                                (offset & s->cluster_offset_mask) & ~511,

                                VAR_7 * 512, abs(VAR_3), VAR_3 < 0,

                                QCOW2_DISCARD_SNAPSHOT);

                            if (VAR_8 < 0) {

                                goto fail;

                            }

                        }

                        

                        refcount = 2;

                        break;



                    case QCOW2_CLUSTER_NORMAL:

                    case QCOW2_CLUSTER_ZERO:

                        if (offset_into_cluster(s, offset & L2E_OFFSET_MASK)) {

                            qcow2_signal_corruption(VAR_0, true, -1, -1, "Data "

                                                    "cluster offset %#llx "

                                                    "unaligned (L2 offset: %#"

                                                    PRIx64 ", L2 index: %#x)",

                                                    offset & L2E_OFFSET_MASK,

                                                    l2_offset, VAR_5);

                            VAR_8 = -EIO;

                            goto fail;

                        }



                        cluster_index = (offset & L2E_OFFSET_MASK) >> s->cluster_bits;

                        if (!cluster_index) {

                            

                            refcount = 0;

                            break;

                        }

                        if (VAR_3 != 0) {

                            VAR_8 = qcow2_update_cluster_refcount(VAR_0,

                                    cluster_index, abs(VAR_3), VAR_3 < 0,

                                    QCOW2_DISCARD_SNAPSHOT);

                            if (VAR_8 < 0) {

                                goto fail;

                            }

                        }



                        VAR_8 = qcow2_get_refcount(VAR_0, cluster_index, &refcount);

                        if (VAR_8 < 0) {

                            goto fail;

                        }

                        break;



                    case QCOW2_CLUSTER_UNALLOCATED:

                        refcount = 0;

                        break;



                    default:

                        abort();

                }



                if (refcount == 1) {

                    offset |= QCOW_OFLAG_COPIED;

                }

                if (offset != old_offset) {

                    if (VAR_3 > 0) {

                        qcow2_cache_set_dependency(VAR_0, s->l2_table_cache,

                            s->refcount_block_cache);

                    }

                    l2_table[VAR_5] = cpu_to_be64(offset);

                    qcow2_cache_entry_mark_dirty(VAR_0, s->l2_table_cache,

                                                 l2_table);

                }

            }



            qcow2_cache_put(VAR_0, s->l2_table_cache, (void **) &l2_table);



            if (VAR_3 != 0) {

                VAR_8 = qcow2_update_cluster_refcount(VAR_0, l2_offset >>

                                                        s->cluster_bits,

                                                    abs(VAR_3), VAR_3 < 0,

                                                    QCOW2_DISCARD_SNAPSHOT);

                if (VAR_8 < 0) {

                    goto fail;

                }

            }

            VAR_8 = qcow2_get_refcount(VAR_0, l2_offset >> s->cluster_bits,

                                     &refcount);

            if (VAR_8 < 0) {

                goto fail;

            } else if (refcount == 1) {

                l2_offset |= QCOW_OFLAG_COPIED;

            }

            if (l2_offset != old_l2_offset) {

                l1_table[VAR_4] = l2_offset;

                VAR_6 = 1;

            }

        }

    }



    VAR_8 = bdrv_flush(VAR_0);

fail:

    if (l2_table) {

        qcow2_cache_put(VAR_0, s->l2_table_cache, (void**) &l2_table);

    }



    s->cache_discards = false;

    qcow2_process_discards(VAR_0, VAR_8);



    

    if (VAR_8 == 0 && VAR_3 >= 0 && VAR_6) {

        for (VAR_4 = 0; VAR_4 < VAR_2; VAR_4++) {

            cpu_to_be64s(&l1_table[VAR_4]);

        }



        VAR_8 = bdrv_pwrite_sync(VAR_0->file, VAR_1,

                               l1_table, l1_size2);



        for (VAR_4 = 0; VAR_4 < VAR_2; VAR_4++) {

            be64_to_cpus(&l1_table[VAR_4]);

        }

    }

    if (l1_allocated)

        g_free(l1_table);

    return VAR_8;

}

int FUNC_0(BlockDriverState *VAR_0,
int64_t VAR_1, int VAR_2, int VAR_3)
{ BDRVQcow2State *s = VAR_0->opaque; uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, refcount; bool l1_allocated = false; int64_t old_offset, old_l2_offset; int VAR_4, VAR_5, VAR_6 = 0, VAR_7; int VAR_8; assert(VAR_3 >= -1 && VAR_3 <= 1); l2_table = NULL; l1_table = NULL; l1_size2 = VAR_2 * sizeof(uint64_t); s->cache_discards = true; if (VAR_1 != s->VAR_1) { l1_table = g_try_malloc0(align_offset(l1_size2, 512)); if (l1_size2 && l1_table == NULL) { VAR_8 = -ENOMEM; goto fail; } l1_allocated = true; VAR_8 = bdrv_pread(VAR_0->file, VAR_1, l1_table, l1_size2); if (VAR_8 < 0) { goto fail; } for(VAR_4 = 0;VAR_4 < VAR_2; VAR_4++) be64_to_cpus(&l1_table[VAR_4]); } else { assert(VAR_2 == s->VAR_2); l1_table = s->l1_table; l1_allocated = false; } for(VAR_4 = 0; VAR_4 < VAR_2; VAR_4++) { l2_offset = l1_table[VAR_4]; if (l2_offset) { old_l2_offset = l2_offset; l2_offset &= L1E_OFFSET_MASK; if (offset_into_cluster(s, l2_offset)) { qcow2_signal_corruption(VAR_0, true, -1, -1, "L2 table offset %#"
PRIx64 " unaligned (L1 index: %#x)",
l2_offset, VAR_4); VAR_8 = -EIO; goto fail; } VAR_8 = qcow2_cache_get(VAR_0, s->l2_table_cache, l2_offset,
(void**) &l2_table); if (VAR_8 < 0) { goto fail; } for(VAR_5 = 0; VAR_5 < s->l2_size; VAR_5++) { uint64_t cluster_index; offset = be64_to_cpu(l2_table[VAR_5]); old_offset = offset; offset &= ~QCOW_OFLAG_COPIED; switch (qcow2_get_cluster_type(offset)) { case QCOW2_CLUSTER_COMPRESSED:
VAR_7 = ((offset >> s->csize_shift) &
s->csize_mask) + 1; if (VAR_3 != 0) { VAR_8 = update_refcount(VAR_0,
(offset & s->cluster_offset_mask) & ~511,
VAR_7 * 512, abs(VAR_3), VAR_3 < 0,
QCOW2_DISCARD_SNAPSHOT); if (VAR_8 < 0) { goto fail; } } refcount = 2; break; case QCOW2_CLUSTER_NORMAL:
case QCOW2_CLUSTER_ZERO:
if (offset_into_cluster(s, offset & L2E_OFFSET_MASK)) { qcow2_signal_corruption(VAR_0, true, -1, -1, "Data "
"cluster offset %#llx "
"unaligned (L2 offset: %#"
PRIx64 ", L2 index: %#x)",
offset & L2E_OFFSET_MASK,
l2_offset, VAR_5); VAR_8 = -EIO; goto fail; } cluster_index = (offset & L2E_OFFSET_MASK) >> s->cluster_bits; if (!cluster_index) { refcount = 0; break; } if (VAR_3 != 0) { VAR_8 = qcow2_update_cluster_refcount(VAR_0,
cluster_index, abs(VAR_3), VAR_3 < 0,
QCOW2_DISCARD_SNAPSHOT); if (VAR_8 < 0) { goto fail; } } VAR_8 = qcow2_get_refcount(VAR_0, cluster_index, &refcount); if (VAR_8 < 0) { goto fail; } break; case QCOW2_CLUSTER_UNALLOCATED:
refcount = 0; break; default:
abort(); } if (refcount == 1) { offset |= QCOW_OFLAG_COPIED; } if (offset != old_offset) { if (VAR_3 > 0) { qcow2_cache_set_dependency(VAR_0, s->l2_table_cache,
s->refcount_block_cache); } l2_table[VAR_5] = cpu_to_be64(offset); qcow2_cache_entry_mark_dirty(VAR_0, s->l2_table_cache,
l2_table); } } qcow2_cache_put(VAR_0, s->l2_table_cache, (void **) &l2_table); if (VAR_3 != 0) { VAR_8 = qcow2_update_cluster_refcount(VAR_0, l2_offset >>
s->cluster_bits,
abs(VAR_3), VAR_3 < 0,
QCOW2_DISCARD_SNAPSHOT); if (VAR_8 < 0) { goto fail; } } VAR_8 = qcow2_get_refcount(VAR_0, l2_offset >> s->cluster_bits,
&refcount); if (VAR_8 < 0) { goto fail; } else if (refcount == 1) { l2_offset |= QCOW_OFLAG_COPIED; } if (l2_offset != old_l2_offset) { l1_table[VAR_4] = l2_offset; VAR_6 = 1; } } } VAR_8 = bdrv_flush(VAR_0); fail:
if (l2_table) { qcow2_cache_put(VAR_0, s->l2_table_cache, (void**) &l2_table); } s->cache_discards = false; qcow2_process_discards(VAR_0, VAR_8); if (VAR_8 == 0 && VAR_3 >= 0 && VAR_6) { for (VAR_4 = 0; VAR_4 < VAR_2; VAR_4++) { cpu_to_be64s(&l1_table[VAR_4]); } VAR_8 = bdrv_pwrite_sync(VAR_0->file, VAR_1,
l1_table, l1_size2); for (VAR_4 = 0; VAR_4 < VAR_2; VAR_4++) { be64_to_cpus(&l1_table[VAR_4]); } } if (l1_allocated)
g_free(l1_table); return VAR_8; }