Commit 812b835f authored by Peter Maydell's avatar Peter Maydell
Browse files

Merge remote-tracking branch 'remotes/maxreitz/tags/pull-block-2019-05-07' into staging



Block patches:
- Fixes to qcow2's implementation of qemu-img check
- Our SSH driver now supports bdrv_refresh_filename()
- Miscellaneous fixes

# gpg: Signature made Tue 07 May 2019 16:16:58 BST
# gpg:                using RSA key F407DB0061D5CF40
# gpg: Good signature from "Max Reitz <mreitz@redhat.com>" [full]
# Primary key fingerprint: 91BE B60A 30DB 3E88 57D1  1829 F407 DB00 61D5 CF40

* remotes/maxreitz/tags/pull-block-2019-05-07:
  iotests: Fix iotests 110 and 126
  commit: Use bdrv_append() in commit_start()
  block: Assert that drv->bdrv_child_perm is set in bdrv_child_perm()
  block/ssh: Implement .bdrv_dirname()
  block/ssh: Implement .bdrv_refresh_filename()
  qcow2: discard bitmap when removed
  qcow2-refcount: don't mask corruptions under internal errors
  qcow2-refcount: check_refcounts_l2: don't count fixed cluster as allocated
  qcow2-refcount: check_refcounts_l2: reduce ignored overlaps
  qcow2-refcount: avoid eating RAM
  qcow2-refcount: fix check_oflag_copied

Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parents 9d86d9ea 1278dce7
Loading
Loading
Loading
Loading
+4 −5
Original line number Diff line number Diff line
@@ -1743,11 +1743,10 @@ static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs,
                            uint64_t parent_perm, uint64_t parent_shared,
                            uint64_t *nperm, uint64_t *nshared)
{
    if (bs->drv && bs->drv->bdrv_child_perm) {
    assert(bs->drv && bs->drv->bdrv_child_perm);
    bs->drv->bdrv_child_perm(bs, c, role, reopen_queue,
                             parent_perm, parent_shared,
                             nperm, nshared);
    }
    /* TODO Take force_share from reopen_queue */
    if (child_bs && child_bs->force_share) {
        *nshared = BLK_PERM_ALL;
+1 −10
Original line number Diff line number Diff line
@@ -303,23 +303,14 @@ void commit_start(const char *job_id, BlockDriverState *bs,
    commit_top_bs->total_sectors = top->total_sectors;
    bdrv_set_aio_context(commit_top_bs, bdrv_get_aio_context(top));

    bdrv_set_backing_hd(commit_top_bs, top, &local_err);
    bdrv_append(commit_top_bs, top, &local_err);
    if (local_err) {
        bdrv_unref(commit_top_bs);
        commit_top_bs = NULL;
        error_propagate(errp, local_err);
        goto fail;
    }
    bdrv_replace_node(top, commit_top_bs, &local_err);
    if (local_err) {
        bdrv_unref(commit_top_bs);
        commit_top_bs = NULL;
        error_propagate(errp, local_err);
        goto fail;
    }

    s->commit_top_bs = commit_top_bs;
    bdrv_unref(commit_top_bs);

    /* Block all nodes between top and base, because they will
     * disappear from the chain after this operation. */
+1 −1
Original line number Diff line number Diff line
@@ -202,7 +202,7 @@ static void clear_bitmap_table(BlockDriverState *bs, uint64_t *bitmap_table,
            continue;
        }

        qcow2_free_clusters(bs, addr, s->cluster_size, QCOW2_DISCARD_OTHER);
        qcow2_free_clusters(bs, addr, s->cluster_size, QCOW2_DISCARD_ALWAYS);
        bitmap_table[i] = 0;
    }
}
+50 −30
Original line number Diff line number Diff line
@@ -1520,12 +1520,31 @@ int qcow2_inc_refcounts_imrt(BlockDriverState *bs, BdrvCheckResult *res,
{
    BDRVQcow2State *s = bs->opaque;
    uint64_t start, last, cluster_offset, k, refcount;
    int64_t file_len;
    int ret;

    if (size <= 0) {
        return 0;
    }

    file_len = bdrv_getlength(bs->file->bs);
    if (file_len < 0) {
        return file_len;
    }

    /*
     * Last cluster of qcow2 image may be semi-allocated, so it may be OK to
     * reference some space after file end but it should be less than one
     * cluster.
     */
    if (offset + size - file_len >= s->cluster_size) {
        fprintf(stderr, "ERROR: counting reference for region exceeding the "
                "end of the file by one cluster or more: offset 0x%" PRIx64
                " size 0x%" PRIx64 "\n", offset, size);
        res->corruptions++;
        return 0;
    }

    start = start_of_cluster(s, offset);
    last = start_of_cluster(s, offset + size - 1);
    for(cluster_offset = start; cluster_offset <= last;
@@ -1572,7 +1591,7 @@ enum {
static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
                              void **refcount_table,
                              int64_t *refcount_table_size, int64_t l2_offset,
                              int flags, BdrvCheckMode fix)
                              int flags, BdrvCheckMode fix, bool active)
{
    BDRVQcow2State *s = bs->opaque;
    uint64_t *l2_table, l2_entry;
@@ -1641,17 +1660,10 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
        {
            uint64_t offset = l2_entry & L2E_OFFSET_MASK;

            if (flags & CHECK_FRAG_INFO) {
                res->bfi.allocated_clusters++;
                if (next_contiguous_offset &&
                    offset != next_contiguous_offset) {
                    res->bfi.fragmented_clusters++;
                }
                next_contiguous_offset = offset + s->cluster_size;
            }

            /* Correct offsets are cluster aligned */
            if (offset_into_cluster(s, offset)) {
                res->corruptions++;

                if (qcow2_get_cluster_type(bs, l2_entry) ==
                    QCOW2_CLUSTER_ZERO_ALLOC)
                {
@@ -1663,11 +1675,12 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
                    if (fix & BDRV_FIX_ERRORS) {
                        uint64_t l2e_offset =
                            l2_offset + (uint64_t)i * sizeof(uint64_t);
                        int ign = active ? QCOW2_OL_ACTIVE_L2 :
                                           QCOW2_OL_INACTIVE_L2;

                        l2_entry = QCOW_OFLAG_ZERO;
                        l2_table[i] = cpu_to_be64(l2_entry);
                        ret = qcow2_pre_write_overlap_check(bs,
                                QCOW2_OL_ACTIVE_L2 | QCOW2_OL_INACTIVE_L2,
                        ret = qcow2_pre_write_overlap_check(bs, ign,
                                l2e_offset, sizeof(uint64_t), false);
                        if (ret < 0) {
                            fprintf(stderr, "ERROR: Overlap check failed\n");
@@ -1686,21 +1699,28 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
                            /* Do not abort, continue checking the rest of this
                             * L2 table's entries */
                        } else {
                            res->corruptions--;
                            res->corruptions_fixed++;
                            /* Skip marking the cluster as used
                             * (it is unused now) */
                            continue;
                        }
                    } else {
                        res->corruptions++;
                    }
                } else {
                    fprintf(stderr, "ERROR offset=%" PRIx64 ": Data cluster is "
                        "not properly aligned; L2 entry corrupted.\n", offset);
                    res->corruptions++;
                }
            }

            if (flags & CHECK_FRAG_INFO) {
                res->bfi.allocated_clusters++;
                if (next_contiguous_offset &&
                    offset != next_contiguous_offset) {
                    res->bfi.fragmented_clusters++;
                }
                next_contiguous_offset = offset + s->cluster_size;
            }

            /* Mark cluster as used */
            if (!has_data_file(bs)) {
                ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table,
@@ -1743,7 +1763,7 @@ static int check_refcounts_l1(BlockDriverState *bs,
                              void **refcount_table,
                              int64_t *refcount_table_size,
                              int64_t l1_table_offset, int l1_size,
                              int flags, BdrvCheckMode fix)
                              int flags, BdrvCheckMode fix, bool active)
{
    BDRVQcow2State *s = bs->opaque;
    uint64_t *l1_table = NULL, l2_offset, l1_size2;
@@ -1799,7 +1819,7 @@ static int check_refcounts_l1(BlockDriverState *bs,
            /* Process and check L2 entries */
            ret = check_refcounts_l2(bs, res, refcount_table,
                                     refcount_table_size, l2_offset, flags,
                                     fix);
                                     fix, active);
            if (ret < 0) {
                goto fail;
            }
@@ -1846,7 +1866,7 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
    for (i = 0; i < s->l1_size; i++) {
        uint64_t l1_entry = s->l1_table[i];
        uint64_t l2_offset = l1_entry & L1E_OFFSET_MASK;
        bool l2_dirty = false;
        int l2_dirty = 0;

        if (!l2_offset) {
            continue;
@@ -1859,6 +1879,7 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
            continue;
        }
        if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) {
            res->corruptions++;
            fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d "
                    "l1_entry=%" PRIx64 " refcount=%" PRIu64 "\n",
                    repair ? "Repairing" : "ERROR", i, l1_entry, refcount);
@@ -1871,9 +1892,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
                    res->check_errors++;
                    goto fail;
                }
                res->corruptions--;
                res->corruptions_fixed++;
            } else {
                res->corruptions++;
            }
        }

@@ -1905,6 +1925,7 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
                    }
                }
                if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) {
                    res->corruptions++;
                    fprintf(stderr, "%s OFLAG_COPIED data cluster: "
                            "l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n",
                            repair ? "Repairing" : "ERROR", l2_entry, refcount);
@@ -1912,16 +1933,13 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
                        l2_table[j] = cpu_to_be64(refcount == 1
                                    ? l2_entry |  QCOW_OFLAG_COPIED
                                    : l2_entry & ~QCOW_OFLAG_COPIED);
                        l2_dirty = true;
                        res->corruptions_fixed++;
                    } else {
                        res->corruptions++;
                        l2_dirty++;
                    }
                }
            }
        }

        if (l2_dirty) {
        if (l2_dirty > 0) {
            ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2,
                                                l2_offset, s->cluster_size,
                                                false);
@@ -1940,6 +1958,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
                res->check_errors++;
                goto fail;
            }
            res->corruptions -= l2_dirty;
            res->corruptions_fixed += l2_dirty;
        }
    }

@@ -1977,6 +1997,7 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res,
        }

        if (cluster >= *nb_clusters) {
            res->corruptions++;
            fprintf(stderr, "%s refcount block %" PRId64 " is outside image\n",
                    fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i);

@@ -2016,6 +2037,7 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res,
                    goto resize_fail;
                }

                res->corruptions--;
                res->corruptions_fixed++;
                ret = qcow2_inc_refcounts_imrt(bs, res,
                                               refcount_table, nb_clusters,
@@ -2029,12 +2051,9 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res,
                continue;

resize_fail:
                res->corruptions++;
                *rebuild = true;
                fprintf(stderr, "ERROR could not resize image: %s\n",
                        strerror(-ret));
            } else {
                res->corruptions++;
            }
            continue;
        }
@@ -2090,7 +2109,7 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
    /* current L1 table */
    ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters,
                             s->l1_table_offset, s->l1_size, CHECK_FRAG_INFO,
                             fix);
                             fix, true);
    if (ret < 0) {
        return ret;
    }
@@ -2119,7 +2138,8 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
            continue;
        }
        ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters,
                                 sn->l1_table_offset, sn->l1_size, 0, fix);
                                 sn->l1_table_offset, sn->l1_size, 0, fix,
                                 false);
        if (ret < 0) {
            return ret;
        }
+68 −5
Original line number Diff line number Diff line
@@ -75,6 +75,14 @@ typedef struct BDRVSSHState {

    /* Used to warn if 'flush' is not supported. */
    bool unsafe_flush_warning;

    /*
     * Store the user name for ssh_refresh_filename() because the
     * default depends on the system you are on -- therefore, when we
     * generate a filename, it should always contain the user name we
     * are actually using.
     */
    char *user;
} BDRVSSHState;

static void ssh_state_init(BDRVSSHState *s)
@@ -87,6 +95,8 @@ static void ssh_state_init(BDRVSSHState *s)

static void ssh_state_free(BDRVSSHState *s)
{
    g_free(s->user);

    if (s->sftp_handle) {
        libssh2_sftp_close(s->sftp_handle);
    }
@@ -628,14 +638,13 @@ static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts,
                          int ssh_flags, int creat_mode, Error **errp)
{
    int r, ret;
    const char *user;
    long port = 0;

    if (opts->has_user) {
        user = opts->user;
        s->user = g_strdup(opts->user);
    } else {
        user = g_get_user_name();
        if (!user) {
        s->user = g_strdup(g_get_user_name());
        if (!s->user) {
            error_setg_errno(errp, errno, "Can't get user name");
            ret = -errno;
            goto err;
@@ -685,7 +694,7 @@ static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts,
    }

    /* Authenticate. */
    ret = authenticate(s, user, errp);
    ret = authenticate(s, s->user, errp);
    if (ret < 0) {
        goto err;
    }
@@ -1242,6 +1251,58 @@ static int coroutine_fn ssh_co_truncate(BlockDriverState *bs, int64_t offset,
    return ssh_grow_file(s, offset, errp);
}

static void ssh_refresh_filename(BlockDriverState *bs)
{
    BDRVSSHState *s = bs->opaque;
    const char *path, *host_key_check;
    int ret;

    /*
     * None of these options can be represented in a plain "host:port"
     * format, so if any was given, we have to abort.
     */
    if (s->inet->has_ipv4 || s->inet->has_ipv6 || s->inet->has_to ||
        s->inet->has_numeric)
    {
        return;
    }

    path = qdict_get_try_str(bs->full_open_options, "path");
    assert(path); /* mandatory option */

    host_key_check = qdict_get_try_str(bs->full_open_options, "host_key_check");

    ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename),
                   "ssh://%s@%s:%s%s%s%s",
                   s->user, s->inet->host, s->inet->port, path,
                   host_key_check ? "?host_key_check=" : "",
                   host_key_check ?: "");
    if (ret >= sizeof(bs->exact_filename)) {
        /* An overflow makes the filename unusable, so do not report any */
        bs->exact_filename[0] = '\0';
    }
}

static char *ssh_bdrv_dirname(BlockDriverState *bs, Error **errp)
{
    if (qdict_haskey(bs->full_open_options, "host_key_check")) {
        /*
         * We cannot generate a simple prefix if we would have to
         * append a query string.
         */
        error_setg(errp,
                   "Cannot generate a base directory with host_key_check set");
        return NULL;
    }

    if (bs->exact_filename[0] == '\0') {
        error_setg(errp, "Cannot generate a base directory for this ssh node");
        return NULL;
    }

    return path_combine(bs->exact_filename, "");
}

static const char *const ssh_strong_runtime_opts[] = {
    "host",
    "port",
@@ -1268,6 +1329,8 @@ static BlockDriver bdrv_ssh = {
    .bdrv_getlength               = ssh_getlength,
    .bdrv_co_truncate             = ssh_co_truncate,
    .bdrv_co_flush_to_disk        = ssh_co_flush,
    .bdrv_refresh_filename        = ssh_refresh_filename,
    .bdrv_dirname                 = ssh_bdrv_dirname,
    .create_opts                  = &ssh_create_opts,
    .strong_runtime_opts          = ssh_strong_runtime_opts,
};
Loading