From 8f52aa40514a290d7770ef2eba3ee3c8b93c1cab Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Fri, 25 Jan 2019 11:48:05 +0300 Subject: [PATCH 1/2] my --- include/qemu-io.h | 7 ++ qemu-img.c | 157 +-------------------------------------- qemu-io-cmds.c | 182 ++++++++++++++++++++++++++++++++++------------ 3 files changed, 145 insertions(+), 201 deletions(-) diff --git a/include/qemu-io.h b/include/qemu-io.h index 7433239372..2abbbb4acf 100644 --- a/include/qemu-io.h +++ b/include/qemu-io.h @@ -54,4 +54,11 @@ void qemuio_complete_command(const char *input, void (*fn)(const char *cmd, void *opaque), void *opaque); +typedef enum OutputFormat { + OFORMAT_JSON, + OFORMAT_HUMAN, +} OutputFormat; + +int qemu_img_do_map(BlockBackend *blk, OutputFormat output_format); + #endif /* QEMU_IO_H */ diff --git a/qemu-img.c b/qemu-img.c index ad04f59565..ecd708af2d 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -45,6 +45,7 @@ #include "block/qapi.h" #include "crypto/init.h" #include "trace/control.h" +#include "qemu-io.h" #define QEMU_IMG_VERSION "qemu-img version " QEMU_FULL_VERSION \ "\n" QEMU_COPYRIGHT "\n" @@ -68,11 +69,6 @@ enum { OPTION_SHRINK = 266, }; -typedef enum OutputFormat { - OFORMAT_JSON, - OFORMAT_HUMAN, -} OutputFormat; - /* Default to cache=writeback as data integrity is not important for qemu-img */ #define BDRV_DEFAULT_CACHE "writeback" @@ -2740,128 +2736,12 @@ static int img_info(int argc, char **argv) return 0; } -static void dump_map_entry(OutputFormat output_format, MapEntry *e, - MapEntry *next) -{ - switch (output_format) { - case OFORMAT_HUMAN: - if (e->data && !e->has_offset) { - error_report("File contains external, encrypted or compressed clusters."); - exit(1); - } - if (e->data && !e->zero) { - printf("%#-16"PRIx64"%#-16"PRIx64"%#-16"PRIx64"%s\n", - e->start, e->length, - e->has_offset ? e->offset : 0, - e->has_filename ? e->filename : ""); - } - /* This format ignores the distinction between 0, ZERO and ZERO|DATA. - * Modify the flags here to allow more coalescing. - */ - if (next && (!next->data || next->zero)) { - next->data = false; - next->zero = true; - } - break; - case OFORMAT_JSON: - printf("%s{ \"start\": %"PRId64", \"length\": %"PRId64"," - " \"depth\": %"PRId64", \"zero\": %s, \"data\": %s", - (e->start == 0 ? "[" : ",\n"), - e->start, e->length, e->depth, - e->zero ? "true" : "false", - e->data ? "true" : "false"); - if (e->has_offset) { - printf(", \"offset\": %"PRId64"", e->offset); - } - putchar('}'); - - if (!next) { - printf("]\n"); - } - break; - } -} - -static int get_block_status(BlockDriverState *bs, int64_t offset, - int64_t bytes, MapEntry *e) -{ - int ret; - int depth; - BlockDriverState *file; - bool has_offset; - int64_t map; - - /* As an optimization, we could cache the current range of unallocated - * clusters in each file of the chain, and avoid querying the same - * range repeatedly. - */ - - depth = 0; - for (;;) { - ret = bdrv_block_status(bs, offset, bytes, &bytes, &map, &file); - if (ret < 0) { - return ret; - } - assert(bytes); - if (ret & (BDRV_BLOCK_ZERO|BDRV_BLOCK_DATA)) { - break; - } - bs = backing_bs(bs); - if (bs == NULL) { - ret = 0; - break; - } - - depth++; - } - - has_offset = !!(ret & BDRV_BLOCK_OFFSET_VALID); - - *e = (MapEntry) { - .start = offset, - .length = bytes, - .data = !!(ret & BDRV_BLOCK_DATA), - .zero = !!(ret & BDRV_BLOCK_ZERO), - .offset = map, - .has_offset = has_offset, - .depth = depth, - .has_filename = file && has_offset, - .filename = file && has_offset ? file->filename : NULL, - }; - - return 0; -} - -static inline bool entry_mergeable(const MapEntry *curr, const MapEntry *next) -{ - if (curr->length == 0) { - return false; - } - if (curr->zero != next->zero || - curr->data != next->data || - curr->depth != next->depth || - curr->has_filename != next->has_filename || - curr->has_offset != next->has_offset) { - return false; - } - if (curr->has_filename && strcmp(curr->filename, next->filename)) { - return false; - } - if (curr->has_offset && curr->offset + curr->length != next->offset) { - return false; - } - return true; -} - static int img_map(int argc, char **argv) { int c; OutputFormat output_format = OFORMAT_HUMAN; BlockBackend *blk; - BlockDriverState *bs; const char *filename, *fmt, *output; - int64_t length; - MapEntry curr = { .length = 0 }, next; int ret = 0; bool image_opts = false; bool force_share = false; @@ -2940,41 +2820,10 @@ static int img_map(int argc, char **argv) if (!blk) { return 1; } - bs = blk_bs(blk); - - if (output_format == OFORMAT_HUMAN) { - printf("%-16s%-16s%-16s%s\n", "Offset", "Length", "Mapped to", "File"); - } - length = blk_getlength(blk); - while (curr.start + curr.length < length) { - int64_t offset = curr.start + curr.length; - int64_t n; - - /* Probe up to 1 GiB at a time. */ - n = MIN(1 << 30, length - offset); - ret = get_block_status(bs, offset, n, &next); - - if (ret < 0) { - error_report("Could not read file metadata: %s", strerror(-ret)); - goto out; - } - - if (entry_mergeable(&curr, &next)) { - curr.length += next.length; - continue; - } - - if (curr.length > 0) { - dump_map_entry(output_format, &curr, &next); - } - curr = next; - } - - dump_map_entry(output_format, &curr, NULL); - -out: + ret = qemu_img_do_map(blk, output_format); blk_unref(blk); + return ret < 0; } diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index 2c39124036..791cedd96d 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -1852,77 +1852,165 @@ static const cmdinfo_t alloc_cmd = { .oneline = "checks if offset is allocated in the file", }; +static void dump_map_entry(OutputFormat output_format, MapEntry *e, + MapEntry *next) +{ + switch (output_format) { + case OFORMAT_HUMAN: + if (e->data && !e->has_offset) { + error_report("File contains external, encrypted or compressed clusters."); + exit(1); + } + if (e->data && !e->zero) { + printf("%#-16"PRIx64"%#-16"PRIx64"%#-16"PRIx64"%s\n", + e->start, e->length, + e->has_offset ? e->offset : 0, + e->has_filename ? e->filename : ""); + } + /* This format ignores the distinction between 0, ZERO and ZERO|DATA. + * Modify the flags here to allow more coalescing. + */ + if (next && (!next->data || next->zero)) { + next->data = false; + next->zero = true; + } + break; + case OFORMAT_JSON: + printf("%s{ \"start\": %"PRId64", \"length\": %"PRId64"," + " \"depth\": %"PRId64", \"zero\": %s, \"data\": %s", + (e->start == 0 ? "[" : ",\n"), + e->start, e->length, e->depth, + e->zero ? "true" : "false", + e->data ? "true" : "false"); + if (e->has_offset) { + printf(", \"offset\": %"PRId64"", e->offset); + } + putchar('}'); -static int map_is_allocated(BlockDriverState *bs, int64_t offset, - int64_t bytes, int64_t *pnum) -{ - int64_t num; - int num_checked; - int ret, firstret; - - num_checked = MIN(bytes, BDRV_REQUEST_MAX_BYTES); - ret = bdrv_is_allocated(bs, offset, num_checked, &num); - if (ret < 0) { - return ret; + if (!next) { + printf("]\n"); + } + break; } +} - firstret = ret; - *pnum = num; - - while (bytes > 0 && ret == firstret) { - offset += num; - bytes -= num; +static int get_block_status(BlockDriverState *bs, int64_t offset, + int64_t bytes, MapEntry *e) +{ + int ret; + int depth; + BlockDriverState *file; + bool has_offset; + int64_t map; + + /* As an optimization, we could cache the current range of unallocated + * clusters in each file of the chain, and avoid querying the same + * range repeatedly. + */ - num_checked = MIN(bytes, BDRV_REQUEST_MAX_BYTES); - ret = bdrv_is_allocated(bs, offset, num_checked, &num); - if (ret == firstret && num) { - *pnum += num; - } else { + depth = 0; + for (;;) { + ret = bdrv_block_status(bs, offset, bytes, &bytes, &map, &file); + if (ret < 0) { + return ret; + } + assert(bytes); + if (ret & (BDRV_BLOCK_ZERO|BDRV_BLOCK_DATA)) { break; } + bs = backing_bs(bs); + if (bs == NULL) { + ret = 0; + break; + } + + depth++; } - return firstret; + has_offset = !!(ret & BDRV_BLOCK_OFFSET_VALID); + + *e = (MapEntry) { + .start = offset, + .length = bytes, + .data = !!(ret & BDRV_BLOCK_DATA), + .zero = !!(ret & BDRV_BLOCK_ZERO), + .offset = map, + .has_offset = has_offset, + .depth = depth, + .has_filename = file && has_offset, + .filename = file && has_offset ? file->filename : NULL, + }; + + return 0; } -static int map_f(BlockBackend *blk, int argc, char **argv) +static inline bool entry_mergeable(const MapEntry *curr, const MapEntry *next) { - int64_t offset, bytes; - char s1[64], s2[64]; - int64_t num; - int ret; - const char *retstr; + if (curr->length == 0) { + return false; + } + if (curr->zero != next->zero || + curr->data != next->data || + curr->depth != next->depth || + curr->has_filename != next->has_filename || + curr->has_offset != next->has_offset) { + return false; + } + if (curr->has_filename && strcmp(curr->filename, next->filename)) { + return false; + } + if (curr->has_offset && curr->offset + curr->length != next->offset) { + return false; + } + return true; +} - offset = 0; - bytes = blk_getlength(blk); - if (bytes < 0) { - error_report("Failed to query image length: %s", strerror(-bytes)); - return bytes; +int qemu_img_do_map(BlockBackend *blk, OutputFormat output_format) +{ + BlockDriverState *bs = blk_bs(blk); + MapEntry curr = { .length = 0 }, next; + int64_t length; + + if (output_format == OFORMAT_HUMAN) { + printf("%-16s%-16s%-16s%s\n", "Offset", "Length", "Mapped to", "File"); } - while (bytes) { - ret = map_is_allocated(blk_bs(blk), offset, bytes, &num); + length = blk_getlength(blk); + while (curr.start + curr.length < length) { + int64_t offset = curr.start + curr.length; + int64_t n; + int ret; + + /* Probe up to 1 GiB at a time. */ + n = MIN(1 << 30, length - offset); + ret = get_block_status(bs, offset, n, &next); + if (ret < 0) { - error_report("Failed to get allocation status: %s", strerror(-ret)); + error_report("Could not read file metadata: %s", strerror(-ret)); return ret; - } else if (!num) { - error_report("Unexpected end of image"); - return -EIO; } - retstr = ret ? " allocated" : "not allocated"; - cvtstr(num, s1, sizeof(s1)); - cvtstr(offset, s2, sizeof(s2)); - printf("%s (0x%" PRIx64 ") bytes %s at offset %s (0x%" PRIx64 ")\n", - s1, num, retstr, s2, offset); + if (entry_mergeable(&curr, &next)) { + curr.length += next.length; + continue; + } - offset += num; - bytes -= num; + if (curr.length > 0) { + dump_map_entry(output_format, &curr, &next); + } + curr = next; } + dump_map_entry(output_format, &curr, NULL); + return 0; } +static int map_f(BlockBackend *blk, int argc, char **argv) +{ + return qemu_img_do_map(blk, OFORMAT_JSON); +} + static const cmdinfo_t map_cmd = { .name = "map", .argmin = 0, -- 2.18.0