diff --git a/block.c b/block.c index 4745712d22e5..b749d31ad49d 100644 --- a/block.c +++ b/block.c @@ -774,6 +774,45 @@ void bdrv_disable_copy_on_read(BlockDriverState *bs) bs->copy_on_read--; } +/* + * Returns the flags that bs->file should get, based on the given flags for + * the parent BDS + */ +static int bdrv_inherited_flags(int flags) +{ + /* Enable protocol handling, disable format probing for bs->file */ + flags |= BDRV_O_PROTOCOL; + + /* Our block drivers take care to send flushes and respect unmap policy, + * so we can enable both unconditionally on lower layers. */ + flags |= BDRV_O_CACHE_WB | BDRV_O_UNMAP; + + /* The backing file of a temporary snapshot is read-only */ + if (flags & BDRV_O_SNAPSHOT) { + flags &= ~BDRV_O_RDWR; + } + + /* Clear flags that only apply to the top layer */ + flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | BDRV_O_COPY_ON_READ); + + return flags; +} + +/* + * Returns the flags that bs->backing_hd should get, based on the given flags + * for the parent BDS + */ +static int bdrv_backing_flags(int flags) +{ + /* backing files always opened read-only */ + flags &= ~(BDRV_O_RDWR | BDRV_O_COPY_ON_READ); + + /* snapshot=on is handled on the top layer */ + flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_TEMPORARY); + + return flags; +} + static int bdrv_open_flags(BlockDriverState *bs, int flags) { int open_flags = flags | BDRV_O_CACHE_WB; @@ -792,7 +831,7 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags) /* * Snapshots should be writable. */ - if (bs->is_temporary) { + if (flags & BDRV_O_TEMPORARY) { open_flags |= BDRV_O_RDWR; } @@ -951,13 +990,6 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, bdrv_refresh_limits(bs); assert(bdrv_opt_mem_align(bs) != 0); assert((bs->request_alignment != 0) || bs->sg); - -#ifndef _WIN32 - if (bs->is_temporary) { - assert(bs->filename[0] != '\0'); - unlink(bs->filename); - } -#endif return 0; free_and_fail: @@ -1069,7 +1101,7 @@ static int bdrv_file_open(BlockDriverState *bs, const char *filename, int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp) { char *backing_filename = g_malloc0(PATH_MAX); - int back_flags, ret = 0; + int ret = 0; BlockDriver *back_drv = NULL; Error *local_err = NULL; @@ -1097,14 +1129,10 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp) back_drv = bdrv_find_format(bs->backing_format); } - /* backing files always opened read-only */ - back_flags = bs->open_flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | - BDRV_O_COPY_ON_READ); - assert(bs->backing_hd == NULL); ret = bdrv_open(&bs->backing_hd, *backing_filename ? backing_filename : NULL, NULL, options, - back_flags, back_drv, &local_err); + bdrv_backing_flags(bs->open_flags), back_drv, &local_err); if (ret < 0) { bs->backing_hd = NULL; bs->open_flags |= BDRV_O_NO_BACKING; @@ -1232,10 +1260,10 @@ void bdrv_append_temp_snapshot(BlockDriverState *bs, Error **errp) qstring_from_str(tmp_filename)); bs_snapshot = bdrv_new("", &error_abort); - bs_snapshot->is_temporary = 1; ret = bdrv_open(&bs_snapshot, NULL, NULL, snapshot_options, - bs->open_flags & ~BDRV_O_SNAPSHOT, bdrv_qcow2, &local_err); + (bs->open_flags & ~BDRV_O_SNAPSHOT) | BDRV_O_TEMPORARY, + bdrv_qcow2, &local_err); if (ret < 0) { error_propagate(errp, local_err); goto out; @@ -1333,10 +1361,10 @@ int bdrv_open(BlockDriverState **pbs, const char *filename, assert(file == NULL); ret = bdrv_open_image(&file, filename, options, "file", - bdrv_open_flags(bs, flags | BDRV_O_UNMAP) | - BDRV_O_PROTOCOL, true, &local_err); + bdrv_inherited_flags(flags), + true, &local_err); if (ret < 0) { - goto unlink_and_fail; + goto fail; } /* Find the right image format driver */ @@ -1347,7 +1375,7 @@ int bdrv_open(BlockDriverState **pbs, const char *filename, if (!drv) { error_setg(errp, "Invalid driver: '%s'", drvname); ret = -EINVAL; - goto unlink_and_fail; + goto fail; } } @@ -1357,18 +1385,18 @@ int bdrv_open(BlockDriverState **pbs, const char *filename, } else { error_setg(errp, "Must specify either driver or file"); ret = -EINVAL; - goto unlink_and_fail; + goto fail; } } if (!drv) { - goto unlink_and_fail; + goto fail; } /* Open the image */ ret = bdrv_open_common(bs, file, options, flags, drv, &local_err); if (ret < 0) { - goto unlink_and_fail; + goto fail; } if (file && (bs->file != file)) { @@ -1430,14 +1458,10 @@ int bdrv_open(BlockDriverState **pbs, const char *filename, *pbs = bs; return 0; -unlink_and_fail: +fail: if (file != NULL) { bdrv_unref(file); } - if (bs->is_temporary) { - unlink(filename); - } -fail: QDECREF(bs->options); QDECREF(options); bs->options = NULL; @@ -1501,8 +1525,11 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, QSIMPLEQ_INIT(bs_queue); } + /* bdrv_open() masks this flag out */ + flags &= ~BDRV_O_PROTOCOL; + if (bs->file) { - bdrv_reopen_queue(bs_queue, bs->file, flags); + bdrv_reopen_queue(bs_queue, bs->file, bdrv_inherited_flags(flags)); } bs_entry = g_new0(BlockReopenQueueEntry, 1); @@ -1717,11 +1744,6 @@ void bdrv_close(BlockDriverState *bs) } bs->drv->bdrv_close(bs); g_free(bs->opaque); -#ifdef _WIN32 - if (bs->is_temporary) { - unlink(bs->filename); - } -#endif bs->opaque = NULL; bs->drv = NULL; bs->copy_on_read = 0; @@ -1845,7 +1867,6 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest, BlockDriverState *bs_src) { /* move some fields that need to stay attached to the device */ - bs_dest->open_flags = bs_src->open_flags; /* dev info */ bs_dest->dev_ops = bs_src->dev_ops; @@ -3601,10 +3622,25 @@ void bdrv_iterate_format(void (*it)(void *opaque, const char *name), void *opaque) { BlockDriver *drv; + int count = 0; + const char **formats = NULL; QLIST_FOREACH(drv, &bdrv_drivers, list) { - it(opaque, drv->format_name); + if (drv->format_name) { + bool found = false; + int i = count; + while (formats && i && !found) { + found = !strcmp(formats[--i], drv->format_name); + } + + if (!found) { + formats = g_realloc(formats, (count + 1) * sizeof(char *)); + formats[count++] = drv->format_name; + it(opaque, drv->format_name); + } + } } + g_free(formats); } /* This function is to find block backend bs */ diff --git a/block/bochs.c b/block/bochs.c index eacf956e7da9..eba23df33537 100644 --- a/block/bochs.c +++ b/block/bochs.c @@ -187,13 +187,14 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) uint64_t offset = sector_num * 512; uint64_t extent_index, extent_offset, bitmap_offset; char bitmap_entry; + int ret; // seek to sector extent_index = offset / s->extent_size; extent_offset = (offset % s->extent_size) / 512; if (s->catalog_bitmap[extent_index] == 0xffffffff) { - return -1; /* not allocated */ + return 0; /* not allocated */ } bitmap_offset = s->data_offset + @@ -201,13 +202,14 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) (s->extent_blocks + s->bitmap_blocks)); /* read in bitmap for current extent */ - if (bdrv_pread(bs->file, bitmap_offset + (extent_offset / 8), - &bitmap_entry, 1) != 1) { - return -1; + ret = bdrv_pread(bs->file, bitmap_offset + (extent_offset / 8), + &bitmap_entry, 1); + if (ret < 0) { + return ret; } if (!((bitmap_entry >> (extent_offset % 8)) & 1)) { - return -1; /* not allocated */ + return 0; /* not allocated */ } return bitmap_offset + (512 * (s->bitmap_blocks + extent_offset)); @@ -220,13 +222,16 @@ static int bochs_read(BlockDriverState *bs, int64_t sector_num, while (nb_sectors > 0) { int64_t block_offset = seek_to_sector(bs, sector_num); - if (block_offset >= 0) { + if (block_offset < 0) { + return block_offset; + } else if (block_offset > 0) { ret = bdrv_pread(bs->file, block_offset, buf, 512); - if (ret != 512) { - return -1; + if (ret < 0) { + return ret; } - } else + } else { memset(buf, 0, 512); + } nb_sectors--; sector_num++; buf += 512; diff --git a/block/cow.c b/block/cow.c index 30deb88deb73..164759f3a3d5 100644 --- a/block/cow.c +++ b/block/cow.c @@ -82,7 +82,7 @@ static int cow_open(BlockDriverState *bs, QDict *options, int flags, if (be32_to_cpu(cow_header.version) != COW_VERSION) { char version[64]; snprintf(version, sizeof(version), - "COW version %d", cow_header.version); + "COW version %" PRIu32, cow_header.version); error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, bs->device_name, "cow", version); ret = -ENOTSUP; diff --git a/block/curl.c b/block/curl.c index 6731d2891f76..d2f1084b913d 100644 --- a/block/curl.c +++ b/block/curl.c @@ -71,6 +71,7 @@ typedef struct CURLState struct BDRVCURLState *s; CURLAIOCB *acb[CURL_NUM_ACB]; CURL *curl; + curl_socket_t sock_fd; char *orig_buf; size_t buf_start; size_t buf_off; @@ -92,6 +93,7 @@ typedef struct BDRVCURLState { static void curl_clean_state(CURLState *s); static void curl_multi_do(void *arg); +static void curl_multi_read(void *arg); #ifdef NEED_CURL_TIMER_CALLBACK static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque) @@ -113,16 +115,20 @@ static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque) static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, void *s, void *sp) { + CURLState *state = NULL; + curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char **)&state); + state->sock_fd = fd; + DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, fd); switch (action) { case CURL_POLL_IN: - qemu_aio_set_fd_handler(fd, curl_multi_do, NULL, s); + qemu_aio_set_fd_handler(fd, curl_multi_read, NULL, state); break; case CURL_POLL_OUT: - qemu_aio_set_fd_handler(fd, NULL, curl_multi_do, s); + qemu_aio_set_fd_handler(fd, NULL, curl_multi_do, state); break; case CURL_POLL_INOUT: - qemu_aio_set_fd_handler(fd, curl_multi_do, curl_multi_do, s); + qemu_aio_set_fd_handler(fd, curl_multi_read, curl_multi_do, state); break; case CURL_POLL_REMOVE: qemu_aio_set_fd_handler(fd, NULL, NULL, NULL); @@ -155,7 +161,7 @@ static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque) DPRINTF("CURL: Just reading %zd bytes\n", realsize); if (!s || !s->orig_buf) - goto read_end; + return 0; if (s->buf_off >= s->buf_len) { /* buffer full, read nothing */ @@ -180,7 +186,6 @@ static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque) } } -read_end: return realsize; } @@ -215,7 +220,8 @@ static int curl_find_buf(BDRVCURLState *s, size_t start, size_t len, } // Wait for unfinished chunks - if ((start >= state->buf_start) && + if (state->in_use && + (start >= state->buf_start) && (start <= buf_fend) && (end >= state->buf_start) && (end <= buf_fend)) @@ -237,68 +243,69 @@ static int curl_find_buf(BDRVCURLState *s, size_t start, size_t len, return FIND_RET_NONE; } -static void curl_multi_read(BDRVCURLState *s) +static void curl_multi_check_completion(BDRVCURLState *s) { int msgs_in_queue; /* Try to find done transfers, so we can free the easy * handle again. */ - do { + for (;;) { CURLMsg *msg; msg = curl_multi_info_read(s->multi, &msgs_in_queue); + /* Quit when there are no more completions */ if (!msg) break; - if (msg->msg == CURLMSG_NONE) - break; - switch (msg->msg) { - case CURLMSG_DONE: - { - CURLState *state = NULL; - curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, (char**)&state); - - /* ACBs for successful messages get completed in curl_read_cb */ - if (msg->data.result != CURLE_OK) { - int i; - for (i = 0; i < CURL_NUM_ACB; i++) { - CURLAIOCB *acb = state->acb[i]; - - if (acb == NULL) { - continue; - } - - acb->common.cb(acb->common.opaque, -EIO); - qemu_aio_release(acb); - state->acb[i] = NULL; + if (msg->msg == CURLMSG_DONE) { + CURLState *state = NULL; + curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, + (char **)&state); + + /* ACBs for successful messages get completed in curl_read_cb */ + if (msg->data.result != CURLE_OK) { + int i; + for (i = 0; i < CURL_NUM_ACB; i++) { + CURLAIOCB *acb = state->acb[i]; + + if (acb == NULL) { + continue; } - } - curl_clean_state(state); - break; + acb->common.cb(acb->common.opaque, -EIO); + qemu_aio_release(acb); + state->acb[i] = NULL; + } } - default: - msgs_in_queue = 0; - break; + + curl_clean_state(state); + break; } - } while(msgs_in_queue); + } } static void curl_multi_do(void *arg) { - BDRVCURLState *s = (BDRVCURLState *)arg; + CURLState *s = (CURLState *)arg; int running; int r; - if (!s->multi) { + if (!s->s->multi) { return; } do { - r = curl_multi_socket_all(s->multi, &running); + r = curl_multi_socket_action(s->s->multi, s->sock_fd, 0, &running); } while(r == CURLM_CALL_MULTI_PERFORM); - curl_multi_read(s); +} + +static void curl_multi_read(void *arg) +{ + CURLState *s = (CURLState *)arg; + + curl_multi_do(arg); + curl_multi_check_completion(s->s); } static void curl_multi_timeout_do(void *arg) @@ -313,7 +320,7 @@ static void curl_multi_timeout_do(void *arg) curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running); - curl_multi_read(s); + curl_multi_check_completion(s); #else abort(); #endif @@ -337,44 +344,42 @@ static CURLState *curl_init_state(BDRVCURLState *s) break; } if (!state) { - g_usleep(100); - curl_multi_do(s); + qemu_aio_wait(); } } while(!state); - if (state->curl) - goto has_curl; - - state->curl = curl_easy_init(); - if (!state->curl) - return NULL; - curl_easy_setopt(state->curl, CURLOPT_URL, s->url); - curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, 5); - curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_read_cb); - curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state); - curl_easy_setopt(state->curl, CURLOPT_PRIVATE, (void *)state); - curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1); - curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1); - curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg); - curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1); - - /* Restrict supported protocols to avoid security issues in the more - * obscure protocols. For example, do not allow POP3/SMTP/IMAP see - * CVE-2013-0249. - * - * Restricting protocols is only supported from 7.19.4 upwards. - */ + if (!state->curl) { + state->curl = curl_easy_init(); + if (!state->curl) { + return NULL; + } + curl_easy_setopt(state->curl, CURLOPT_URL, s->url); + curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, 5); + curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, + (void *)curl_read_cb); + curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state); + curl_easy_setopt(state->curl, CURLOPT_PRIVATE, (void *)state); + curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1); + curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg); + curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1); + + /* Restrict supported protocols to avoid security issues in the more + * obscure protocols. For example, do not allow POP3/SMTP/IMAP see + * CVE-2013-0249. + * + * Restricting protocols is only supported from 7.19.4 upwards. + */ #if LIBCURL_VERSION_NUM >= 0x071304 - curl_easy_setopt(state->curl, CURLOPT_PROTOCOLS, PROTOCOLS); - curl_easy_setopt(state->curl, CURLOPT_REDIR_PROTOCOLS, PROTOCOLS); + curl_easy_setopt(state->curl, CURLOPT_PROTOCOLS, PROTOCOLS); + curl_easy_setopt(state->curl, CURLOPT_REDIR_PROTOCOLS, PROTOCOLS); #endif #ifdef DEBUG_VERBOSE - curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1); + curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1); #endif - -has_curl: + } state->s = s; @@ -531,13 +536,11 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, // initialize the multi interface! s->multi = curl_multi_init(); - curl_multi_setopt(s->multi, CURLMOPT_SOCKETDATA, s); curl_multi_setopt(s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb); #ifdef NEED_CURL_TIMER_CALLBACK curl_multi_setopt(s->multi, CURLMOPT_TIMERDATA, s); curl_multi_setopt(s->multi, CURLMOPT_TIMERFUNCTION, curl_timer_cb); #endif - curl_multi_do(s); qemu_opts_del(opts); return 0; @@ -566,6 +569,7 @@ static const AIOCBInfo curl_aiocb_info = { static void curl_readv_bh_cb(void *p) { CURLState *state; + int running; CURLAIOCB *acb = p; BDRVCURLState *s = acb->common.bs->opaque; @@ -614,8 +618,9 @@ static void curl_readv_bh_cb(void *p) curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range); curl_multi_add_handle(s->multi, state->curl); - curl_multi_do(s); + /* Tell curl it needs to kick things off */ + curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running); } static BlockDriverAIOCB *curl_aio_readv(BlockDriverState *bs, diff --git a/block/dmg.c b/block/dmg.c index 856402e1f2fe..1e153cd76d05 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -248,8 +248,8 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, offset += 8; if (s->sectorcounts[i] > DMG_SECTORCOUNTS_MAX) { - error_report("sector count %" PRIu64 " for chunk %u is " - "larger than max (%u)", + error_report("sector count %" PRIu64 " for chunk %" PRIu32 + " is larger than max (%u)", s->sectorcounts[i], i, DMG_SECTORCOUNTS_MAX); ret = -EINVAL; goto fail; @@ -269,8 +269,8 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, offset += 8; if (s->lengths[i] > DMG_LENGTHS_MAX) { - error_report("length %" PRIu64 " for chunk %u is larger " - "than max (%u)", + error_report("length %" PRIu64 " for chunk %" PRIu32 + " is larger than max (%u)", s->lengths[i], i, DMG_LENGTHS_MAX); ret = -EINVAL; goto fail; diff --git a/block/mirror.c b/block/mirror.c index 36f4f8e8bd86..1c38aa8f7700 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -325,11 +325,11 @@ static void coroutine_fn mirror_run(void *opaque) s->common.len = bdrv_getlength(bs); if (s->common.len <= 0) { - block_job_completed(&s->common, s->common.len); - return; + ret = s->common.len; + goto immediate_exit; } - length = (bdrv_getlength(bs) + s->granularity - 1) / s->granularity; + length = DIV_ROUND_UP(s->common.len, s->granularity); s->in_flight_bitmap = bitmap_new(length); /* If we have no backing file yet in the destination, we cannot let @@ -339,7 +339,10 @@ static void coroutine_fn mirror_run(void *opaque) bdrv_get_backing_filename(s->target, backing_filename, sizeof(backing_filename)); if (backing_filename[0] && !s->target->backing_hd) { - bdrv_get_info(s->target, &bdi); + ret = bdrv_get_info(s->target, &bdi); + if (ret < 0) { + goto immediate_exit; + } if (s->granularity < bdi.cluster_size) { s->buf_size = MAX(s->buf_size, bdi.cluster_size); s->cow_bitmap = bitmap_new(length); diff --git a/block/qapi.c b/block/qapi.c index 8f2b4dbe7d07..af114452e0b6 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -532,12 +532,11 @@ static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation, void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f, ImageInfoSpecific *info_spec) { - Error *local_err = NULL; QmpOutputVisitor *ov = qmp_output_visitor_new(); QObject *obj, *data; visit_type_ImageInfoSpecific(qmp_output_get_visitor(ov), &info_spec, NULL, - &local_err); + &error_abort); obj = qmp_output_get_qobject(ov); assert(qobject_type(obj) == QTYPE_QDICT); data = qdict_get(qobject_to_qdict(obj), "data"); diff --git a/block/qcow.c b/block/qcow.c index d5a7d5fd1edd..937dd6dd1c38 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -119,7 +119,8 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, } if (header.version != QCOW_VERSION) { char version[64]; - snprintf(version, sizeof(version), "QCOW version %d", header.version); + snprintf(version, sizeof(version), "QCOW version %" PRIu32, + header.version); error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, bs->device_name, "qcow", version); ret = -ENOTSUP; diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 331ab0802280..76d2bcf63ad5 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -42,6 +42,13 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, if (min_size <= s->l1_size) return 0; + /* Do a sanity check on min_size before trying to calculate new_l1_size + * (this prevents overflows during the while loop for the calculation of + * new_l1_size) */ + if (min_size > INT_MAX / sizeof(uint64_t)) { + return -EFBIG; + } + if (exact_size) { new_l1_size = min_size; } else { @@ -1360,9 +1367,9 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); for (i = 0; i < nb_clusters; i++) { - uint64_t old_offset; + uint64_t old_l2_entry; - old_offset = be64_to_cpu(l2_table[l2_index + i]); + old_l2_entry = be64_to_cpu(l2_table[l2_index + i]); /* * Make sure that a discarded area reads back as zeroes for v3 images @@ -1373,12 +1380,22 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, * TODO We might want to use bdrv_get_block_status(bs) here, but we're * holding s->lock, so that doesn't work today. */ - if (old_offset & QCOW_OFLAG_ZERO) { - continue; - } + switch (qcow2_get_cluster_type(old_l2_entry)) { + case QCOW2_CLUSTER_UNALLOCATED: + if (!bs->backing_hd) { + continue; + } + break; - if ((old_offset & L2E_OFFSET_MASK) == 0 && !bs->backing_hd) { - continue; + case QCOW2_CLUSTER_ZERO: + continue; + + case QCOW2_CLUSTER_NORMAL: + case QCOW2_CLUSTER_COMPRESSED: + break; + + default: + abort(); } /* First remove L2 entries */ @@ -1390,7 +1407,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, } /* Then decrease the refcount */ - qcow2_free_any_clusters(bs, old_offset, 1, type); + qcow2_free_any_clusters(bs, old_l2_entry, 1, type); } ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index a37ee45016f6..e79895d11dcb 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -653,6 +653,13 @@ static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size) goto retry; } } + + /* Make sure that all offsets in the "allocated" range are representable + * in an int64_t */ + if (s->free_cluster_index - 1 > (INT64_MAX >> s->cluster_bits)) { + return -EFBIG; + } + #ifdef DEBUG_ALLOC2 fprintf(stderr, "alloc_clusters: size=%" PRId64 " -> %" PRId64 "\n", size, @@ -1480,6 +1487,11 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, int ret; size = bdrv_getlength(bs->file); + if (size < 0) { + res->check_errors++; + return size; + } + nb_clusters = size_to_clusters(s, size); if (nb_clusters > INT_MAX) { res->check_errors++; diff --git a/block/qcow2.c b/block/qcow2.c index e903d971c307..a4b97e8263c3 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -124,8 +124,9 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, case QCOW2_EXT_MAGIC_BACKING_FORMAT: if (ext.len >= sizeof(bs->backing_format)) { - error_setg(errp, "ERROR: ext_backing_format: len=%u too large" - " (>=%zu)", ext.len, sizeof(bs->backing_format)); + error_setg(errp, "ERROR: ext_backing_format: len=%" PRIu32 + " too large (>=%zu)", ext.len, + sizeof(bs->backing_format)); return 2; } ret = bdrv_pread(bs->file, offset, bs->backing_format, ext.len); @@ -483,7 +484,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } if (header.version < 2 || header.version > 3) { - report_unsupported(bs, errp, "QCOW version %d", header.version); + report_unsupported(bs, errp, "QCOW version %" PRIu32, header.version); ret = -ENOTSUP; goto fail; } @@ -493,7 +494,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, /* Initialise cluster size */ if (header.cluster_bits < MIN_CLUSTER_BITS || header.cluster_bits > MAX_CLUSTER_BITS) { - error_setg(errp, "Unsupported cluster size: 2^%i", header.cluster_bits); + error_setg(errp, "Unsupported cluster size: 2^%" PRIu32, + header.cluster_bits); ret = -EINVAL; goto fail; } @@ -591,7 +593,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, s->refcount_order = header.refcount_order; if (header.crypt_method > QCOW_CRYPT_AES) { - error_setg(errp, "Unsupported encryption method: %i", + error_setg(errp, "Unsupported encryption method: %" PRIu32, header.crypt_method); ret = -EINVAL; goto fail; diff --git a/block/raw-posix.c b/block/raw-posix.c index 1688e16c6481..3ce026db589a 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -366,7 +366,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, BDRVRawState *s = bs->opaque; QemuOpts *opts; Error *local_err = NULL; - const char *filename; + const char *filename = NULL; int fd, ret; struct stat st; @@ -446,6 +446,9 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, ret = 0; fail: + if (filename && (bdrv_flags & BDRV_O_TEMPORARY)) { + unlink(filename); + } qemu_opts_del(opts); return ret; } diff --git a/block/raw-win32.c b/block/raw-win32.c index 48cb2c22587e..064ea3123c81 100644 --- a/block/raw-win32.c +++ b/block/raw-win32.c @@ -390,6 +390,9 @@ static void raw_close(BlockDriverState *bs) { BDRVRawState *s = bs->opaque; CloseHandle(s->hfile); + if (bs->open_flags & BDRV_O_TEMPORARY) { + unlink(bs->filename); + } } static int raw_truncate(BlockDriverState *bs, int64_t offset) diff --git a/block/sheepdog.c b/block/sheepdog.c index 0eb33ee80e26..2c3fb016a8f8 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -1099,7 +1099,7 @@ static int find_vdi_name(BDRVSheepdogState *s, const char *filename, } if (rsp->result != SD_RES_SUCCESS) { - error_report("cannot get vdi info, %s, %s %d %s", + error_report("cannot get vdi info, %s, %s %" PRIu32 " %s", sd_strerror(rsp->result), filename, snapid, tag); if (rsp->result == SD_RES_NO_VDI) { ret = -ENOENT; @@ -2316,8 +2316,8 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) sn_tab[found].vm_state_size = inode.vm_state_size; sn_tab[found].vm_clock_nsec = inode.vm_clock_nsec; - snprintf(sn_tab[found].id_str, sizeof(sn_tab[found].id_str), "%u", - inode.snap_id); + snprintf(sn_tab[found].id_str, sizeof(sn_tab[found].id_str), + "%" PRIu32, inode.snap_id); pstrcpy(sn_tab[found].name, MIN(sizeof(sn_tab[found].name), sizeof(inode.tag)), inode.tag); diff --git a/block/vdi.c b/block/vdi.c index 820cd376b325..27737af555b0 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -408,34 +408,35 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, } if (header.signature != VDI_SIGNATURE) { - error_setg(errp, "Image not in VDI format (bad signature %08x)", header.signature); + error_setg(errp, "Image not in VDI format (bad signature %08" PRIx32 + ")", header.signature); ret = -EINVAL; goto fail; } else if (header.version != VDI_VERSION_1_1) { - error_setg(errp, "unsupported VDI image (version %u.%u)", - header.version >> 16, header.version & 0xffff); + error_setg(errp, "unsupported VDI image (version %" PRIu32 ".%" PRIu32 + ")", header.version >> 16, header.version & 0xffff); ret = -ENOTSUP; goto fail; } else if (header.offset_bmap % SECTOR_SIZE != 0) { /* We only support block maps which start on a sector boundary. */ error_setg(errp, "unsupported VDI image (unaligned block map offset " - "0x%x)", header.offset_bmap); + "0x%" PRIx32 ")", header.offset_bmap); ret = -ENOTSUP; goto fail; } else if (header.offset_data % SECTOR_SIZE != 0) { /* We only support data blocks which start on a sector boundary. */ - error_setg(errp, "unsupported VDI image (unaligned data offset 0x%x)", - header.offset_data); + error_setg(errp, "unsupported VDI image (unaligned data offset 0x%" + PRIx32 ")", header.offset_data); ret = -ENOTSUP; goto fail; } else if (header.sector_size != SECTOR_SIZE) { - error_setg(errp, "unsupported VDI image (sector size %u is not %u)", - header.sector_size, SECTOR_SIZE); + error_setg(errp, "unsupported VDI image (sector size %" PRIu32 + " is not %u)", header.sector_size, SECTOR_SIZE); ret = -ENOTSUP; goto fail; } else if (header.block_size != DEFAULT_CLUSTER_SIZE) { - error_setg(errp, "unsupported VDI image (block size %u is not %u)", - header.block_size, DEFAULT_CLUSTER_SIZE); + error_setg(errp, "unsupported VDI image (block size %" PRIu32 + " is not %u)", header.block_size, DEFAULT_CLUSTER_SIZE); ret = -ENOTSUP; goto fail; } else if (header.disk_size > @@ -755,6 +756,7 @@ static int vdi_create(const char *filename, QEMUOptionParameter *options, vdi_header_to_le(&header); if (write(fd, &header, sizeof(header)) < 0) { result = -errno; + goto close_and_exit; } if (bmap_size > 0) { @@ -768,6 +770,8 @@ static int vdi_create(const char *filename, QEMUOptionParameter *options, } if (write(fd, bmap, bmap_size) < 0) { result = -errno; + g_free(bmap); + goto close_and_exit; } g_free(bmap); } @@ -775,10 +779,12 @@ static int vdi_create(const char *filename, QEMUOptionParameter *options, if (image_type == VDI_TYPE_STATIC) { if (ftruncate(fd, sizeof(header) + bmap_size + blocks * block_size)) { result = -errno; + goto close_and_exit; } } - if (close(fd) < 0) { +close_and_exit: + if ((close(fd) < 0) && !result) { result = -errno; } diff --git a/include/block/block.h b/include/block/block.h index c12808a25212..467fb2ba0a13 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -92,6 +92,7 @@ typedef enum { #define BDRV_O_RDWR 0x0002 #define BDRV_O_SNAPSHOT 0x0008 /* open the file read only and save writes in a snapshot */ +#define BDRV_O_TEMPORARY 0x0010 /* delete the file after use */ #define BDRV_O_NOCACHE 0x0020 /* do not use the host page cache */ #define BDRV_O_CACHE_WB 0x0040 /* use write-back caching */ #define BDRV_O_NATIVE_AIO 0x0080 /* use native AIO instead of the thread pool */ diff --git a/include/block/block_int.h b/include/block/block_int.h index cd5bc7308ade..9ffcb698d0fa 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -299,7 +299,6 @@ struct BlockDriverState { char backing_file[1024]; /* if non zero, the image is a diff of this file image */ char backing_format[16]; /* if non-zero and backing_file exists */ - int is_temporary; BlockDriverState *backing_hd; BlockDriverState *file; diff --git a/qemu-img.c b/qemu-img.c index 968b4c8e83c5..96f44638b77e 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -33,6 +33,9 @@ #include "block/qapi.h" #include +#define QEMU_IMG_VERSION "qemu-img version " QEMU_VERSION \ + ", Copyright (c) 2004-2008 Fabrice Bellard\n" + typedef struct img_cmd_t { const char *name; int (*handler)(int argc, char **argv); @@ -75,7 +78,7 @@ static void QEMU_NORETURN GCC_FMT_ATTR(1, 2) error_exit(const char *fmt, ...) static void QEMU_NORETURN help(void) { const char *help_msg = - "qemu-img version " QEMU_VERSION ", Copyright (c) 2004-2008 Fabrice Bellard\n" + QEMU_IMG_VERSION "usage: qemu-img command [command options]\n" "QEMU disk image utility\n" "\n" @@ -2789,6 +2792,12 @@ int main(int argc, char **argv) { const img_cmd_t *cmd; const char *cmdname; + int c; + static const struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'v'}, + {0, 0, 0, 0} + }; #ifdef CONFIG_POSIX signal(SIGPIPE, SIG_IGN); @@ -2803,15 +2812,24 @@ int main(int argc, char **argv) error_exit("Not enough arguments"); } cmdname = argv[1]; - argc--; argv++; /* find the command */ - for(cmd = img_cmds; cmd->name != NULL; cmd++) { + for (cmd = img_cmds; cmd->name != NULL; cmd++) { if (!strcmp(cmdname, cmd->name)) { - return cmd->handler(argc, argv); + return cmd->handler(argc - 1, argv + 1); } } + c = getopt_long(argc, argv, "h", long_options, NULL); + + if (c == 'h') { + help(); + } + if (c == 'v') { + printf(QEMU_IMG_VERSION); + return 0; + } + /* not found */ error_exit("Command not found: %s", cmdname); } diff --git a/tests/qemu-iotests/019 b/tests/qemu-iotests/019 index e67445c75497..f5ecbf545143 100755 --- a/tests/qemu-iotests/019 +++ b/tests/qemu-iotests/019 @@ -96,7 +96,7 @@ mv "$TEST_IMG" "$TEST_IMG.orig" for backing_option in "-B " "-o backing_file="; do echo - echo Testing conversion with $backing_option$TEST_IMG.base | _filter_testdir | _filter_imgfmt + echo Testing conversion with $backing_option"$TEST_IMG.base" | _filter_testdir | _filter_imgfmt echo $QEMU_IMG convert -O $IMGFMT $backing_option"$TEST_IMG.base" "$TEST_IMG.orig" "$TEST_IMG" diff --git a/tests/qemu-iotests/086 b/tests/qemu-iotests/086 index 48fe85bc437b..d9a80cf8638b 100755 --- a/tests/qemu-iotests/086 +++ b/tests/qemu-iotests/086 @@ -51,10 +51,10 @@ function run_qemu_img() size=128M _make_test_img $size -$QEMU_IO -c 'write 0 1M' $TEST_IMG | _filter_qemu_io -$QEMU_IO -c 'write 2M 1M' $TEST_IMG | _filter_qemu_io -$QEMU_IO -c 'write 4M 1M' $TEST_IMG | _filter_qemu_io -$QEMU_IO -c 'write 32M 1M' $TEST_IMG | _filter_qemu_io +$QEMU_IO -c 'write 0 1M' "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c 'write 2M 1M' "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c 'write 4M 1M' "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c 'write 32M 1M' "$TEST_IMG" | _filter_qemu_io $QEMU_IMG convert -p -O $IMGFMT -f $IMGFMT "$TEST_IMG" "$TEST_IMG".base 2>&1 |\ _filter_testdir | sed -e 's/\r/\n/g' diff --git a/tests/qemu-iotests/090 b/tests/qemu-iotests/090 new file mode 100755 index 000000000000..8d032f811142 --- /dev/null +++ b/tests/qemu-iotests/090 @@ -0,0 +1,61 @@ +#!/bin/bash +# +# Test for discarding compressed clusters on qcow2 images +# +# Copyright (C) 2014 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=mreitz@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +IMG_SIZE=128K + +_make_test_img $IMG_SIZE + +$QEMU_IO -c 'write -c -P 42 0 64k' \ + -c 'write -c -P 23 64k 64k' \ + -c 'discard 64k 64k' \ + "$TEST_IMG" | _filter_qemu_io + +$QEMU_IO -c 'read -P 0 64k 64k' "$TEST_IMG" | _filter_qemu_io + +_check_test_img + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/090.out b/tests/qemu-iotests/090.out new file mode 100644 index 000000000000..2df93e0d9708 --- /dev/null +++ b/tests/qemu-iotests/090.out @@ -0,0 +1,12 @@ +QA output created by 090 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +discard 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. +*** done diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 7f00883cad18..195c5646aa92 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -178,10 +178,10 @@ _rm_test_img() local img=$1 if [ "$IMGFMT" = "vmdk" ]; then # Remove all the extents for vmdk - $QEMU_IMG info $img 2>/dev/null | grep 'filename:' | cut -f 2 -d: \ + "$QEMU_IMG" info "$img" 2>/dev/null | grep 'filename:' | cut -f 2 -d: \ | xargs -I {} rm -f "{}" fi - rm -f $img + rm -f "$img" } _cleanup_test_img() diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 864643d256f8..ae096638b867 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -95,3 +95,4 @@ 086 rw auto quick 087 rw auto 088 rw auto +090 rw auto quick