Commit ca8c0fab authored by Peter Maydell's avatar Peter Maydell
Browse files

Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging



Block patches

# gpg: Signature made Mon 19 May 2014 15:21:14 BST using RSA key ID C88F2FD6
# gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>"

* remotes/kevin/tags/for-upstream: (22 commits)
  block: optimize zero writes with bdrv_write_zeroes
  blockdev: add a function to parse enum ids from strings
  util: add qemu_iovec_is_zero
  qcow1: Stricter backing file length check
  qcow1: Validate image size (CVE-2014-0223)
  qcow1: Validate L2 table size (CVE-2014-0222)
  qcow1: Check maximum cluster size
  qcow1: Make padding in the header explicit
  curl: Add usage documentation
  curl: Add sslverify option
  curl: Remove broken parsing of options from url
  curl: Fix build when curl_multi_socket_action isn't available
  qemu-iotests: Fix blkdebug in VM drive in 030
  qemu-iotests: Fix core dump suppression in test 039
  iotests: Add test for the JSON protocol
  block: Allow JSON filenames
  check-qdict: Add test for qdict_join()
  qdict: Add qdict_join()
  block: add test for vhdx image created by Disk2VHD
  block: vhdx - account for identical header sections
  ...

Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parents c5fa6c86 465bee1d
Loading
Loading
Loading
Loading
+56 −4
Original line number Diff line number Diff line
@@ -1274,6 +1274,33 @@ out:
    g_free(tmp_filename);
}

static QDict *parse_json_filename(const char *filename, Error **errp)
{
    QObject *options_obj;
    QDict *options;
    int ret;

    ret = strstart(filename, "json:", &filename);
    assert(ret);

    options_obj = qobject_from_json(filename);
    if (!options_obj) {
        error_setg(errp, "Could not parse the JSON options");
        return NULL;
    }

    if (qobject_type(options_obj) != QTYPE_QDICT) {
        qobject_decref(options_obj);
        error_setg(errp, "Invalid JSON object given");
        return NULL;
    }

    options = qobject_to_qdict(options_obj);
    qdict_flatten(options);

    return options;
}

/*
 * Opens a disk image (raw, qcow2, vmdk, ...)
 *
@@ -1337,6 +1364,20 @@ int bdrv_open(BlockDriverState **pbs, const char *filename,
        options = qdict_new();
    }

    if (filename && g_str_has_prefix(filename, "json:")) {
        QDict *json_options = parse_json_filename(filename, &local_err);
        if (local_err) {
            ret = -EINVAL;
            goto fail;
        }

        /* Options given in the filename have lower priority than options
         * specified directly */
        qdict_join(options, json_options, false);
        QDECREF(json_options);
        filename = NULL;
    }

    bs->options = options;
    options = qdict_clone_shallow(options);

@@ -3248,6 +3289,15 @@ static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs,

    ret = notifier_with_return_list_notify(&bs->before_write_notifiers, req);

    if (!ret && bs->detect_zeroes != BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF &&
        !(flags & BDRV_REQ_ZERO_WRITE) && drv->bdrv_co_write_zeroes &&
        qemu_iovec_is_zero(qiov)) {
        flags |= BDRV_REQ_ZERO_WRITE;
        if (bs->detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP) {
            flags |= BDRV_REQ_MAY_UNMAP;
        }
    }

    if (ret < 0) {
        /* Do nothing, write notifier decided to fail this request */
    } else if (flags & BDRV_REQ_ZERO_WRITE) {
@@ -3864,7 +3914,7 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,

    if (!bs->drv->bdrv_co_get_block_status) {
        *pnum = nb_sectors;
        ret = BDRV_BLOCK_DATA;
        ret = BDRV_BLOCK_DATA | BDRV_BLOCK_ALLOCATED;
        if (bs->drv->protocol_name) {
            ret |= BDRV_BLOCK_OFFSET_VALID | (sector_num * BDRV_SECTOR_SIZE);
        }
@@ -3883,6 +3933,10 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
                                     *pnum, pnum);
    }

    if (ret & (BDRV_BLOCK_DATA | BDRV_BLOCK_ZERO)) {
        ret |= BDRV_BLOCK_ALLOCATED;
    }

    if (!(ret & BDRV_BLOCK_DATA) && !(ret & BDRV_BLOCK_ZERO)) {
        if (bdrv_unallocated_blocks_are_zero(bs)) {
            ret |= BDRV_BLOCK_ZERO;
@@ -3959,9 +4013,7 @@ int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num,
    if (ret < 0) {
        return ret;
    }
    return
        (ret & BDRV_BLOCK_DATA) ||
        ((ret & BDRV_BLOCK_ZERO) && !bdrv_has_zero_init(bs));
    return (ret & BDRV_BLOCK_ALLOCATED);
}

/*
+37 −42
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@
 */
#include "qemu-common.h"
#include "block/block_int.h"
#include "qapi/qmp/qbool.h"
#include <curl/curl.h>

// #define DEBUG
@@ -37,6 +38,21 @@
#if LIBCURL_VERSION_NUM >= 0x071000
/* The multi interface timer callback was introduced in 7.16.0 */
#define NEED_CURL_TIMER_CALLBACK
#define HAVE_SOCKET_ACTION
#endif

#ifndef HAVE_SOCKET_ACTION
/* If curl_multi_socket_action isn't available, define it statically here in
 * terms of curl_multi_socket. Note that ev_bitmask will be ignored, which is
 * less efficient but still safe. */
static CURLMcode __curl_multi_socket_action(CURLM *multi_handle,
                                            curl_socket_t sockfd,
                                            int ev_bitmask,
                                            int *running_handles)
{
    return curl_multi_socket(multi_handle, sockfd, running_handles);
}
#define curl_multi_socket_action __curl_multi_socket_action
#endif

#define PROTOCOLS (CURLPROTO_HTTP | CURLPROTO_HTTPS | \
@@ -46,12 +62,16 @@
#define CURL_NUM_STATES 8
#define CURL_NUM_ACB    8
#define SECTOR_SIZE     512
#define READ_AHEAD_SIZE (256 * 1024)
#define READ_AHEAD_DEFAULT (256 * 1024)

#define FIND_RET_NONE   0
#define FIND_RET_OK     1
#define FIND_RET_WAIT   2

#define CURL_BLOCK_OPT_URL       "url"
#define CURL_BLOCK_OPT_READAHEAD "readahead"
#define CURL_BLOCK_OPT_SSLVERIFY "sslverify"

struct BDRVCURLState;

typedef struct CURLAIOCB {
@@ -88,6 +108,7 @@ typedef struct BDRVCURLState {
    CURLState states[CURL_NUM_STATES];
    char *url;
    size_t readahead_size;
    bool sslverify;
    bool accept_range;
} BDRVCURLState;

@@ -354,6 +375,8 @@ static CURLState *curl_init_state(BDRVCURLState *s)
            return NULL;
        }
        curl_easy_setopt(state->curl, CURLOPT_URL, s->url);
        curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER,
                         (long) s->sslverify);
        curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, 5);
        curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION,
                         (void *)curl_read_cb);
@@ -396,43 +419,7 @@ static void curl_clean_state(CURLState *s)
static void curl_parse_filename(const char *filename, QDict *options,
                                Error **errp)
{

    #define RA_OPTSTR ":readahead="
    char *file;
    char *ra;
    const char *ra_val;
    int parse_state = 0;

    file = g_strdup(filename);

    /* Parse a trailing ":readahead=#:" param, if present. */
    ra = file + strlen(file) - 1;
    while (ra >= file) {
        if (parse_state == 0) {
            if (*ra == ':') {
                parse_state++;
            } else {
                break;
            }
        } else if (parse_state == 1) {
            if (*ra > '9' || *ra < '0') {
                char *opt_start = ra - strlen(RA_OPTSTR) + 1;
                if (opt_start > file &&
                    strncmp(opt_start, RA_OPTSTR, strlen(RA_OPTSTR)) == 0) {
                    ra_val = ra + 1;
                    ra -= strlen(RA_OPTSTR) - 1;
                    *ra = '\0';
                    qdict_put(options, "readahead", qstring_from_str(ra_val));
                }
                break;
            }
        }
        ra--;
    }

    qdict_put(options, "url", qstring_from_str(file));

    g_free(file);
    qdict_put(options, CURL_BLOCK_OPT_URL, qstring_from_str(filename));
}

static QemuOptsList runtime_opts = {
@@ -440,15 +427,20 @@ static QemuOptsList runtime_opts = {
    .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
    .desc = {
        {
            .name = "url",
            .name = CURL_BLOCK_OPT_URL,
            .type = QEMU_OPT_STRING,
            .help = "URL to open",
        },
        {
            .name = "readahead",
            .name = CURL_BLOCK_OPT_READAHEAD,
            .type = QEMU_OPT_SIZE,
            .help = "Readahead size",
        },
        {
            .name = CURL_BLOCK_OPT_SSLVERIFY,
            .type = QEMU_OPT_BOOL,
            .help = "Verify SSL certificate"
        },
        { /* end of list */ }
    },
};
@@ -477,14 +469,17 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
        goto out_noclean;
    }

    s->readahead_size = qemu_opt_get_size(opts, "readahead", READ_AHEAD_SIZE);
    s->readahead_size = qemu_opt_get_size(opts, CURL_BLOCK_OPT_READAHEAD,
                                          READ_AHEAD_DEFAULT);
    if ((s->readahead_size & 0x1ff) != 0) {
        error_setg(errp, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512",
                   s->readahead_size);
        goto out_noclean;
    }

    file = qemu_opt_get(opts, "url");
    s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY, true);

    file = qemu_opt_get(opts, CURL_BLOCK_OPT_URL);
    if (file == NULL) {
        error_setg(errp, "curl block driver requires an 'url' option");
        goto out_noclean;
+1 −0
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs)
    }

    info->backing_file_depth = bdrv_get_backing_file_depth(bs);
    info->detect_zeroes = bs->detect_zeroes;

    if (bs->io_limits_enabled) {
        ThrottleConfig cfg;
+37 −7
Original line number Diff line number Diff line
@@ -48,9 +48,10 @@ typedef struct QCowHeader {
    uint64_t size; /* in bytes */
    uint8_t cluster_bits;
    uint8_t l2_bits;
    uint16_t padding;
    uint32_t crypt_method;
    uint64_t l1_table_offset;
} QCowHeader;
} QEMU_PACKED QCowHeader;

#define L2_CACHE_SIZE 16

@@ -60,7 +61,7 @@ typedef struct BDRVQcowState {
    int cluster_sectors;
    int l2_bits;
    int l2_size;
    int l1_size;
    unsigned int l1_size;
    uint64_t cluster_offset_mask;
    uint64_t l1_table_offset;
    uint64_t *l1_table;
@@ -96,7 +97,8 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
                     Error **errp)
{
    BDRVQcowState *s = bs->opaque;
    int len, i, shift, ret;
    unsigned int len, i, shift;
    int ret;
    QCowHeader header;

    ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
@@ -127,11 +129,25 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
        goto fail;
    }

    if (header.size <= 1 || header.cluster_bits < 9) {
        error_setg(errp, "invalid value in qcow header");
    if (header.size <= 1) {
        error_setg(errp, "Image size is too small (must be at least 2 bytes)");
        ret = -EINVAL;
        goto fail;
    }
    if (header.cluster_bits < 9 || header.cluster_bits > 16) {
        error_setg(errp, "Cluster size must be between 512 and 64k");
        ret = -EINVAL;
        goto fail;
    }

    /* l2_bits specifies number of entries; storing a uint64_t in each entry,
     * so bytes = num_entries << 3. */
    if (header.l2_bits < 9 - 3 || header.l2_bits > 16 - 3) {
        error_setg(errp, "L2 table size must be between 512 and 64k");
        ret = -EINVAL;
        goto fail;
    }

    if (header.crypt_method > QCOW_CRYPT_AES) {
        error_setg(errp, "invalid encryption method in qcow header");
        ret = -EINVAL;
@@ -151,7 +167,19 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,

    /* read the level 1 table */
    shift = s->cluster_bits + s->l2_bits;
    s->l1_size = (header.size + (1LL << shift) - 1) >> shift;
    if (header.size > UINT64_MAX - (1LL << shift)) {
        error_setg(errp, "Image too large");
        ret = -EINVAL;
        goto fail;
    } else {
        uint64_t l1_size = (header.size + (1LL << shift) - 1) >> shift;
        if (l1_size > INT_MAX / sizeof(uint64_t)) {
            error_setg(errp, "Image too large");
            ret = -EINVAL;
            goto fail;
        }
        s->l1_size = l1_size;
    }

    s->l1_table_offset = header.l1_table_offset;
    s->l1_table = g_malloc(s->l1_size * sizeof(uint64_t));
@@ -175,7 +203,9 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
    if (header.backing_file_offset != 0) {
        len = header.backing_file_size;
        if (len > 1023) {
            len = 1023;
            error_setg(errp, "Backing file name too long");
            ret = -EINVAL;
            goto fail;
        }
        ret = bdrv_pread(bs->file, header.backing_file_offset,
                   bs->backing_file, len);
+8 −1
Original line number Diff line number Diff line
@@ -472,10 +472,17 @@ static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s,
            s->curr_header = 0;
        } else if (h2_seq > h1_seq) {
            s->curr_header = 1;
        } else {
            /* The Microsoft Disk2VHD tool will create 2 identical
             * headers, with identical sequence numbers.  If the headers are
             * identical, don't consider the file corrupt */
            if (!memcmp(header1, header2, sizeof(VHDXHeader))) {
                s->curr_header = 0;
            } else {
                goto fail;
            }
        }
    }

    vhdx_region_register(s, s->headers[s->curr_header]->log_offset,
                            s->headers[s->curr_header]->log_length);
Loading