static void perform_test(const char *truth_file, const char *test_file, const char *format, int compare_before, int compare_after) { int flags, i; bs = bdrv_new ("hda"); if (!bs) { die ("bdrv_new failed\n"); } BlockDriver *drv = NULL; if (format) { drv = bdrv_find_format (format); if (!drv) { die ("Found no driver for format '%s'.\n", format); } } flags = BDRV_O_RDWR | BDRV_O_CACHE_WB; if (bdrv_open (bs, test_file, flags, drv) < 0) { die ("Failed to open '%s'\n", test_file); } fd = open (truth_file, O_RDWR | O_LARGEFILE, 0); if (fd < 0) { perror ("open"); die ("Failed to open '%s'\n", truth_file); } int64_t l0 = lseek (fd, 0, SEEK_END); int64_t l1 = bdrv_getlength (bs); if (l0 < 0 || l1 < 0 || l0 < l1) { die ("Mismatch: truth image %s length %" PRId64 ", test image %s " "length %" PRId64 "\n", truth_file, l0, test_file, l1); } total_sectors = l1 / 512; if (total_sectors <= 1) { die ("Total sectors: %" PRId64 "\n", total_sectors); } io_size /= 512; if (io_size <= 0) { io_size = 1; } else if (io_size > total_sectors / 2) { io_size = total_sectors / 2; } if (compare_before) { if (compare_full_images ()) { die ("The original two files do not match.\n"); } } if (round > 0) { /* Create testers. */ testers = g_malloc(sizeof(RandomIO) * parallel); for (i = 0; i < parallel; i++) { RandomIO *r = &testers[i]; r->test_buf = qemu_blockalign (bs, io_size * 512); if (posix_memalign ((void **) &r->truth_buf, 512, io_size * 512)) { die ("posix_memalign"); } r->qiov.iov = g_malloc(sizeof(struct iovec) * max_iov); r->sector_num = 0; r->nb_sectors = 0; r->type = OP_READ; r->tester = i; } for (i = 0; i < parallel; i++) { perform_next_io (&testers[i]); } } sim_all_tasks (); /* Run tests. */ if (round > 0) { /* Create testers. */ if (compare_after) { if (compare_full_images ()) { die ("The two files do not match after I/O operations.\n"); } } for (i = 0; i < parallel; i++) { RandomIO *r = &testers[i]; qemu_vfree (r->test_buf); free (r->truth_buf); g_free(r->qiov.iov); } g_free(testers); } printf ("Test process %d finished successfully\n", getpid ()); int fvd = (strncmp (bs->drv->format_name, "fvd", 3) == 0); bdrv_delete (bs); if (fvd) { fvd_check_memory_usage (); } close (fd); }
static int img_create(int argc, char **argv) { int c, ret, flags; const char *fmt = "raw"; const char *base_fmt = NULL; const char *filename; const char *base_filename = NULL; BlockDriver *drv; QEMUOptionParameter *param = NULL; char *options = NULL; flags = 0; for(;;) { c = getopt(argc, argv, "F:b:f:he6o:"); if (c == -1) break; switch(c) { case 'h': help(); break; case 'F': base_fmt = optarg; break; case 'b': base_filename = optarg; break; case 'f': fmt = optarg; break; case 'e': flags |= BLOCK_FLAG_ENCRYPT; break; case '6': flags |= BLOCK_FLAG_COMPAT6; break; case 'o': options = optarg; break; } } /* Find driver and parse its options */ drv = bdrv_find_format(fmt); if (!drv) error("Unknown file format '%s'", fmt); if (options && !strcmp(options, "?")) { print_option_help(drv->create_options); return 0; } /* Create parameter list with default values */ param = parse_option_parameters("", drv->create_options, param); set_option_parameter_int(param, BLOCK_OPT_SIZE, -1); /* Parse -o options */ if (options) { param = parse_option_parameters(options, drv->create_options, param); if (param == NULL) { error("Invalid options for file format '%s'.", fmt); } } /* Get the filename */ if (optind >= argc) help(); filename = argv[optind++]; /* Add size to parameters */ if (optind < argc) { set_option_parameter(param, BLOCK_OPT_SIZE, argv[optind++]); } /* Add old-style options to parameters */ add_old_style_options(fmt, param, flags, base_filename, base_fmt); // The size for the image must always be specified, with one exception: // If we are using a backing file, we can obtain the size from there if (get_option_parameter(param, BLOCK_OPT_SIZE)->value.n == -1) { QEMUOptionParameter *backing_file = get_option_parameter(param, BLOCK_OPT_BACKING_FILE); QEMUOptionParameter *backing_fmt = get_option_parameter(param, BLOCK_OPT_BACKING_FMT); if (backing_file && backing_file->value.s) { BlockDriverState *bs; uint64_t size; const char *fmt = NULL; char buf[32]; if (backing_fmt && backing_fmt->value.s) { if (bdrv_find_format(backing_fmt->value.s)) { fmt = backing_fmt->value.s; } else { error("Unknown backing file format '%s'", backing_fmt->value.s); } } bs = bdrv_new_open(backing_file->value.s, fmt); bdrv_get_geometry(bs, &size); size *= 512; bdrv_delete(bs); snprintf(buf, sizeof(buf), "%" PRId64, size); set_option_parameter(param, BLOCK_OPT_SIZE, buf); } else { error("Image creation needs a size parameter"); } } printf("Formatting '%s', fmt=%s ", filename, fmt); print_option_parameters(param); puts(""); ret = bdrv_create(drv, filename, param); free_option_parameters(param); if (ret < 0) { if (ret == -ENOTSUP) { error("Formatting or formatting option not supported for file format '%s'", fmt); } else if (ret == -EFBIG) { error("The image size is too large for file format '%s'", fmt); } else { error("Error while formatting"); } } return 0; }
/* * Checks an image for consistency. Exit codes: * * 0 - Check completed, image is good * 1 - Check not completed because of internal errors * 2 - Check completed, image is corrupted * 3 - Check completed, image has leaked clusters, but is good otherwise */ static int img_check(int argc, char **argv) { int c, ret; const char *filename, *fmt; BlockDriverState *bs; BdrvCheckResult result; fmt = NULL; for(;;) { c = getopt(argc, argv, "f:h"); if (c == -1) { break; } switch(c) { case '?': case 'h': help(); break; case 'f': fmt = optarg; break; } } if (optind >= argc) { help(); } filename = argv[optind++]; bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS); if (!bs) { return 1; } ret = bdrv_check(bs, &result); if (ret == -ENOTSUP) { error_report("This image format does not support checks"); bdrv_delete(bs); return 1; } if (!(result.corruptions || result.leaks || result.check_errors)) { printf("No errors were found on the image.\n"); } else { if (result.corruptions) { printf("\n%d errors were found on the image.\n" "Data may be corrupted, or further writes to the image " "may corrupt it.\n", result.corruptions); } if (result.leaks) { printf("\n%d leaked clusters were found on the image.\n" "This means waste of disk space, but no harm to data.\n", result.leaks); } if (result.check_errors) { printf("\n%d internal errors have occurred during the check.\n", result.check_errors); } } bdrv_delete(bs); if (ret < 0 || result.check_errors) { printf("\nAn error has occurred during the check: %s\n" "The check is not complete and may have missed error.\n", strerror(-ret)); return 1; } if (result.corruptions) { return 2; } else if (result.leaks) { return 3; } else { return 0; } }
static int blk_init(struct XenDevice *xendev) { struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); int mode, qflags, have_barriers, info = 0; char *h = NULL; /* read xenstore entries */ if (blkdev->params == NULL) { blkdev->params = xenstore_read_be_str(&blkdev->xendev, "params"); if (blkdev->params != NULL) h = strchr(blkdev->params, ':'); if (h != NULL) { blkdev->fileproto = blkdev->params; blkdev->filename = h+1; *h = 0; } else { blkdev->fileproto = "<unset>"; blkdev->filename = blkdev->params; } } if (!strcmp("aio", blkdev->fileproto)) blkdev->fileproto = "raw"; if (blkdev->mode == NULL) blkdev->mode = xenstore_read_be_str(&blkdev->xendev, "mode"); if (blkdev->type == NULL) blkdev->type = xenstore_read_be_str(&blkdev->xendev, "type"); if (blkdev->dev == NULL) blkdev->dev = xenstore_read_be_str(&blkdev->xendev, "dev"); if (blkdev->devtype == NULL) blkdev->devtype = xenstore_read_be_str(&blkdev->xendev, "device-type"); /* do we have all we need? */ if (blkdev->params == NULL || blkdev->mode == NULL || blkdev->type == NULL || blkdev->dev == NULL) return -1; /* read-only ? */ qflags = BDRV_O_NOCACHE; if (strcmp(blkdev->mode, "w") == 0) { mode = O_RDWR; qflags |= BDRV_O_RDWR; } else { mode = O_RDONLY; qflags |= BDRV_O_RDONLY; info |= VDISK_READONLY; } /* cdrom ? */ if (blkdev->devtype && !strcmp(blkdev->devtype, "cdrom")) info |= VDISK_CDROM; /* init qemu block driver */ blkdev->index = (blkdev->xendev.dev - 202 * 256) / 16; blkdev->index = drive_get_index(IF_XEN, 0, blkdev->index); if (blkdev->index == -1) { /* setup via xenbus -> create new block driver instance */ xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n"); blkdev->bs = bdrv_new(blkdev->dev); if (blkdev->bs) { if (bdrv_open2(blkdev->bs, blkdev->filename, qflags, bdrv_find_format(blkdev->fileproto)) != 0) { bdrv_delete(blkdev->bs); blkdev->bs = NULL; } } if (!blkdev->bs) return -1; } else { /* setup via qemu cmdline -> already setup for us */ xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n"); blkdev->bs = drives_table[blkdev->index].bdrv; } blkdev->file_blk = BLOCK_SIZE; blkdev->file_size = bdrv_getlength(blkdev->bs); if (blkdev->file_size < 0) { xen_be_printf(&blkdev->xendev, 1, "bdrv_getlength: %d (%s) | drv %s\n", (int)blkdev->file_size, strerror(-blkdev->file_size), blkdev->bs->drv ? blkdev->bs->drv->format_name : "-"); blkdev->file_size = 0; } have_barriers = blkdev->bs->drv && blkdev->bs->drv->bdrv_flush ? 1 : 0; xen_be_printf(xendev, 1, "type \"%s\", fileproto \"%s\", filename \"%s\"," " size %" PRId64 " (%" PRId64 " MB)\n", blkdev->type, blkdev->fileproto, blkdev->filename, blkdev->file_size, blkdev->file_size >> 20); /* fill info */ xenstore_write_be_int(&blkdev->xendev, "feature-barrier", have_barriers); xenstore_write_be_int(&blkdev->xendev, "info", info); xenstore_write_be_int(&blkdev->xendev, "sector-size", blkdev->file_blk); xenstore_write_be_int(&blkdev->xendev, "sectors", blkdev->file_size / blkdev->file_blk); return 0; }
static int img_create(int argc, char **argv) { int c, ret, flags; const char *fmt = "raw"; const char *filename; const char *base_filename = NULL; uint64_t size; const char *p; BlockDriver *drv; flags = 0; for(;;) { c = getopt(argc, argv, "b:f:he6"); if (c == -1) break; switch(c) { case 'h': help(); break; case 'b': base_filename = optarg; break; case 'f': fmt = optarg; break; case 'e': flags |= BLOCK_FLAG_ENCRYPT; break; case '6': flags |= BLOCK_FLAG_COMPAT6; break; } } if (optind >= argc) help(); filename = argv[optind++]; size = 0; if (base_filename) { BlockDriverState *bs; bs = bdrv_new_open(base_filename, NULL, BDRV_O_RDWR); bdrv_get_geometry(bs, &size); size *= 512; bdrv_delete(bs); } else { if (optind >= argc) help(); p = argv[optind]; size = strtoul(p, (char **)&p, 0); if (*p == 'M') { size *= 1024 * 1024; } else if (*p == 'G') { size *= 1024 * 1024 * 1024; } else if (*p == 'k' || *p == 'K' || *p == '\0') { size *= 1024; } else { help(); } } drv = bdrv_find_format(fmt); if (!drv) error("Unknown file format '%s'", fmt); printf("Formatting '%s', fmt=%s", filename, fmt); if (flags & BLOCK_FLAG_ENCRYPT) printf(", encrypted"); if (flags & BLOCK_FLAG_COMPAT6) printf(", compatibility level=6"); if (base_filename) { printf(", backing_file=%s", base_filename); } printf(", size=%" PRIu64 " kB\n", size / 1024); ret = bdrv_create(drv, filename, size / 512, base_filename, flags); if (ret < 0) { if (ret == -ENOTSUP) { error("Formatting or formatting option not supported for file format '%s'", fmt); } else { error("Error while formatting"); } } return 0; }
int main(int argc, char *argv[]) { int c; const char *filename, *fmt; BlockDriver *drv; BlockDriverState *bs; char fmt_name[128], size_buf[128], dsize_buf[128]; uint64_t total_sectors; int64_t allocated_size; char backing_filename[1024]; char backing_filename2[1024]; BlockDriverInfo bdi; bdrv_init(); fmt = NULL; for(;;) { c = getopt(argc, argv, "f:h"); if (c == -1) break; switch(c) { case 'h': // help(); break; case 'f': fmt = optarg; break; } } if (optind >= argc) help(); filename = argv[optind++]; bs = bdrv_new(""); if (!bs) error("Not enough memory"); if (fmt) { drv = bdrv_find_format(fmt); if (!drv) error("Unknown file format '%s'", fmt); } else { drv = NULL; } if (bdrv_open2(bs, filename, 0, drv) < 0) { error("Could not open '%s'", filename); } bdrv_get_format(bs, fmt_name, sizeof(fmt_name)); bdrv_get_geometry(bs, &total_sectors); get_human_readable_size(size_buf, sizeof(size_buf), total_sectors * 512); allocated_size = get_allocated_file_size(filename); if (allocated_size < 0) sprintf(dsize_buf, "unavailable"); else get_human_readable_size(dsize_buf, sizeof(dsize_buf), allocated_size); /* if (bdrv_is_encrypted(bs)) fprintf(stderr, "encrypted: yes\n"); if (bdrv_get_info(bs, &bdi) >= 0) { if (bdi.cluster_size != 0) fprintf(stderr, "cluster_size: %d\n", bdi.cluster_size); } */ bdrv_get_info(bs, &bdi); bdrv_get_backing_filename(bs, backing_filename, sizeof(backing_filename)); if (backing_filename[0] != '\0') { path_combine(backing_filename2, sizeof(backing_filename2), filename, backing_filename); /* fprintf(stderr, "backing file: %s (actual path: %s)\n", backing_filename, backing_filename2); */ } fprintf(stdout, "{'filename' : '%s'," " 'format' : '%s'," " 'image_disk_size' : '%s'," " 'allocated_size' : '%s'," " 'total_sectors' : '%"PRId64"'," " 'backing_file' : '%s',}", filename, fmt_name, size_buf, dsize_buf, total_sectors, backing_filename); dump_snapshots(bs); bdrv_delete(bs); return 0; }
static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, const char *desc_file_path) { int ret; char access[11]; char type[11]; char fname[512]; const char *p = desc; int64_t sectors = 0; int64_t flat_offset; char extent_path[PATH_MAX]; BlockDriverState *extent_file; while (*p) { /* parse extent line: * RW [size in sectors] FLAT "file-name.vmdk" OFFSET * or * RW [size in sectors] SPARSE "file-name.vmdk" */ flat_offset = -1; ret = sscanf(p, "%10s %" SCNd64 " %10s %511s %" SCNd64, access, §ors, type, fname, &flat_offset); if (ret < 4 || strcmp(access, "RW")) { goto next_line; } else if (!strcmp(type, "FLAT")) { if (ret != 5 || flat_offset < 0) { return -EINVAL; } } else if (ret != 4) { return -EINVAL; } /* trim the quotation marks around */ if (fname[0] == '"') { memmove(fname, fname + 1, strlen(fname)); if (strlen(fname) <= 1 || fname[strlen(fname) - 1] != '"') { return -EINVAL; } fname[strlen(fname) - 1] = '\0'; } if (sectors <= 0 || (strcmp(type, "FLAT") && strcmp(type, "SPARSE")) || (strcmp(access, "RW"))) { goto next_line; } path_combine(extent_path, sizeof(extent_path), desc_file_path, fname); ret = bdrv_file_open(&extent_file, extent_path, bs->open_flags); if (ret) { return ret; } /* save to extents array */ if (!strcmp(type, "FLAT")) { /* FLAT extent */ VmdkExtent *extent; extent = vmdk_add_extent(bs, extent_file, true, sectors, 0, 0, 0, 0, sectors); extent->flat_start_offset = flat_offset << 9; } else if (!strcmp(type, "SPARSE")) { /* SPARSE extent */ ret = vmdk_open_sparse(bs, extent_file, bs->open_flags); if (ret) { bdrv_delete(extent_file); return ret; } } else { fprintf(stderr, "VMDK: Not supported extent type \"%s\""".\n", type); return -ENOTSUP; } next_line: /* move to next line */ while (*p && *p != '\n') { p++; } p++; } return 0; }
int main(int argc, char **argv) { int readonly = 0; int growable = 0; const char *sopt = "hVc:rsnmgkt:T:"; const struct option lopt[] = { { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'V' }, { "offset", 1, NULL, 'o' }, { "cmd", 1, NULL, 'c' }, { "read-only", 0, NULL, 'r' }, { "snapshot", 0, NULL, 's' }, { "nocache", 0, NULL, 'n' }, { "misalign", 0, NULL, 'm' }, { "growable", 0, NULL, 'g' }, { "native-aio", 0, NULL, 'k' }, { "cache", 1, NULL, 't' }, { "trace", 1, NULL, 'T' }, { NULL, 0, NULL, 0 } }; int c; int opt_index = 0; int flags = 0; progname = basename(argv[0]); while ((c = getopt_long(argc, argv, sopt, lopt, &opt_index)) != -1) { switch (c) { case 's': flags |= BDRV_O_SNAPSHOT; break; case 'n': flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB; break; case 'c': add_user_command(optarg); break; case 'r': readonly = 1; break; case 'm': misalign = 1; break; case 'g': growable = 1; break; case 'k': flags |= BDRV_O_NATIVE_AIO; break; case 't': if (bdrv_parse_cache_flags(optarg, &flags) < 0) { error_report("Invalid cache option: %s", optarg); exit(1); } break; case 'T': if (!trace_backend_init(optarg, NULL)) { exit(1); /* error message will have been printed */ } break; case 'V': printf("%s version %s\n", progname, VERSION); exit(0); case 'h': usage(progname); exit(0); default: usage(progname); exit(1); } } if ((argc - optind) > 1) { usage(progname); exit(1); } bdrv_init(); qemu_init_main_loop(); /* initialize commands */ quit_init(); help_init(); add_command(&open_cmd); add_command(&close_cmd); add_command(&read_cmd); add_command(&readv_cmd); add_command(&write_cmd); add_command(&writev_cmd); add_command(&multiwrite_cmd); add_command(&aio_read_cmd); add_command(&aio_write_cmd); add_command(&aio_flush_cmd); add_command(&flush_cmd); add_command(&truncate_cmd); add_command(&length_cmd); add_command(&info_cmd); add_command(&discard_cmd); add_command(&alloc_cmd); add_command(&map_cmd); add_args_command(init_args_command); add_check_command(init_check_command); /* open the device */ if (!readonly) { flags |= BDRV_O_RDWR; } if ((argc - optind) == 1) { openfile(argv[optind], flags, growable); } command_loop(); /* * Make sure all outstanding requests complete before the program exits. */ bdrv_drain_all(); if (bs) { bdrv_delete(bs); } return 0; }
static int close_f(int argc, char **argv) { bdrv_delete(bs); bs = NULL; return 0; }
static void coroutine_fn mirror_run(void *opaque) { MirrorBlockJob *s = opaque; BlockDriverState *bs = s->common.bs; int64_t sector_num, end, sectors_per_chunk, length; uint64_t last_pause_ns; BlockDriverInfo bdi; char backing_filename[1024]; int ret = 0; int n; if (block_job_is_cancelled(&s->common)) { goto immediate_exit; } s->common.len = bdrv_getlength(bs); if (s->common.len <= 0) { block_job_completed(&s->common, s->common.len); return; } length = (bdrv_getlength(bs) + s->granularity - 1) / s->granularity; s->in_flight_bitmap = bitmap_new(length); /* If we have no backing file yet in the destination, we cannot let * the destination do COW. Instead, we copy sectors around the * dirty data if needed. We need a bitmap to do that. */ 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); if (s->granularity < bdi.cluster_size) { s->buf_size = MAX(s->buf_size, bdi.cluster_size); s->cow_bitmap = bitmap_new(length); } } end = s->common.len >> BDRV_SECTOR_BITS; s->buf = qemu_blockalign(bs, s->buf_size); sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS; mirror_free_init(s); if (s->mode != MIRROR_SYNC_MODE_NONE) { /* First part, loop on the sectors and initialize the dirty bitmap. */ BlockDriverState *base; base = s->mode == MIRROR_SYNC_MODE_FULL ? NULL : bs->backing_hd; for (sector_num = 0; sector_num < end; ) { int64_t next = (sector_num | (sectors_per_chunk - 1)) + 1; ret = bdrv_co_is_allocated_above(bs, base, sector_num, next - sector_num, &n); if (ret < 0) { goto immediate_exit; } assert(n > 0); if (ret == 1) { bdrv_set_dirty(bs, sector_num, n); sector_num = next; } else { sector_num += n; } } } bdrv_dirty_iter_init(bs, &s->hbi); last_pause_ns = qemu_get_clock_ns(rt_clock); for (;;) { uint64_t delay_ns; int64_t cnt; bool should_complete; if (s->ret < 0) { ret = s->ret; goto immediate_exit; } cnt = bdrv_get_dirty_count(bs); /* Note that even when no rate limit is applied we need to yield * periodically with no pending I/O so that qemu_aio_flush() returns. * We do so every SLICE_TIME nanoseconds, or when there is an error, * or when the source is clean, whichever comes first. */ if (qemu_get_clock_ns(rt_clock) - last_pause_ns < SLICE_TIME && s->common.iostatus == BLOCK_DEVICE_IO_STATUS_OK) { if (s->in_flight == MAX_IN_FLIGHT || s->buf_free_count == 0 || (cnt == 0 && s->in_flight > 0)) { trace_mirror_yield(s, s->in_flight, s->buf_free_count, cnt); qemu_coroutine_yield(); continue; } else if (cnt != 0) { mirror_iteration(s); continue; } } should_complete = false; if (s->in_flight == 0 && cnt == 0) { trace_mirror_before_flush(s); ret = bdrv_flush(s->target); if (ret < 0) { if (mirror_error_action(s, false, -ret) == BDRV_ACTION_REPORT) { goto immediate_exit; } } else { /* We're out of the streaming phase. From now on, if the job * is cancelled we will actually complete all pending I/O and * report completion. This way, block-job-cancel will leave * the target in a consistent state. */ s->common.offset = end * BDRV_SECTOR_SIZE; if (!s->synced) { block_job_ready(&s->common); s->synced = true; } should_complete = s->should_complete || block_job_is_cancelled(&s->common); cnt = bdrv_get_dirty_count(bs); } } if (cnt == 0 && should_complete) { /* The dirty bitmap is not updated while operations are pending. * If we're about to exit, wait for pending operations before * calling bdrv_get_dirty_count(bs), or we may exit while the * source has dirty data to copy! * * Note that I/O can be submitted by the guest while * mirror_populate runs. */ trace_mirror_before_drain(s, cnt); bdrv_drain_all(); cnt = bdrv_get_dirty_count(bs); } ret = 0; trace_mirror_before_sleep(s, cnt, s->synced); if (!s->synced) { /* Publish progress */ s->common.offset = (end - cnt) * BDRV_SECTOR_SIZE; if (s->common.speed) { delay_ns = ratelimit_calculate_delay(&s->limit, sectors_per_chunk); } else { delay_ns = 0; } block_job_sleep_ns(&s->common, rt_clock, delay_ns); if (block_job_is_cancelled(&s->common)) { break; } } else if (!should_complete) { delay_ns = (s->in_flight == 0 && cnt == 0 ? SLICE_TIME : 0); block_job_sleep_ns(&s->common, rt_clock, delay_ns); } else if (cnt == 0) { /* The two disks are in sync. Exit and report successful * completion. */ assert(QLIST_EMPTY(&bs->tracked_requests)); s->common.cancelled = false; break; } last_pause_ns = qemu_get_clock_ns(rt_clock); } immediate_exit: if (s->in_flight > 0) { /* We get here only if something went wrong. Either the job failed, * or it was cancelled prematurely so that we do not guarantee that * the target is a copy of the source. */ assert(ret < 0 || (!s->synced && block_job_is_cancelled(&s->common))); mirror_drain(s); } assert(s->in_flight == 0); qemu_vfree(s->buf); g_free(s->cow_bitmap); g_free(s->in_flight_bitmap); bdrv_set_dirty_tracking(bs, 0); bdrv_iostatus_disable(s->target); if (s->should_complete && ret == 0) { if (bdrv_get_flags(s->target) != bdrv_get_flags(s->common.bs)) { bdrv_reopen(s->target, bdrv_get_flags(s->common.bs), NULL); } bdrv_swap(s->target, s->common.bs); } bdrv_close(s->target); bdrv_delete(s->target); block_job_completed(&s->common, ret); }
static int img_commit(int argc, char **argv) { int c, ret, flags; const char *filename, *fmt, *cache; BlockDriverState *bs; fmt = NULL; cache = BDRV_DEFAULT_CACHE; for(;;) { c = getopt(argc, argv, "f:ht:"); if (c == -1) { break; } switch(c) { case '?': case 'h': help(); break; case 'f': fmt = optarg; break; case 't': cache = optarg; break; } } if (optind >= argc) { help(); } filename = argv[optind++]; flags = BDRV_O_RDWR; ret = bdrv_parse_cache_flags(cache, &flags); if (ret < 0) { error_report("Invalid cache option: %s", cache); return -1; } bs = bdrv_new_open(filename, fmt, flags); if (!bs) { return 1; } ret = bdrv_commit(bs); switch(ret) { case 0: printf("Image committed.\n"); break; case -ENOENT: error_report("No disk inserted"); break; case -EACCES: error_report("Image is read-only"); break; case -ENOTSUP: error_report("Image is already committed"); break; default: error_report("Error while committing image"); break; } bdrv_delete(bs); if (ret) { return 1; } return 0; }