Commit faea38e7863a6e29f110063388eb93840fcd475c
1 parent
42ca6388
multiple snapshot support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2086 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
6 changed files
with
711 additions
and
139 deletions
block.c
| @@ -494,12 +494,10 @@ int bdrv_write(BlockDriverState *bs, int64_t sector_num, | @@ -494,12 +494,10 @@ int bdrv_write(BlockDriverState *bs, int64_t sector_num, | ||
| 494 | } | 494 | } |
| 495 | } | 495 | } |
| 496 | 496 | ||
| 497 | -#if 0 | ||
| 498 | /* not necessary now */ | 497 | /* not necessary now */ |
| 499 | static int bdrv_pread_em(BlockDriverState *bs, int64_t offset, | 498 | static int bdrv_pread_em(BlockDriverState *bs, int64_t offset, |
| 500 | - void *buf1, int count1) | 499 | + uint8_t *buf, int count1) |
| 501 | { | 500 | { |
| 502 | - uint8_t *buf = buf1; | ||
| 503 | uint8_t tmp_buf[SECTOR_SIZE]; | 501 | uint8_t tmp_buf[SECTOR_SIZE]; |
| 504 | int len, nb_sectors, count; | 502 | int len, nb_sectors, count; |
| 505 | int64_t sector_num; | 503 | int64_t sector_num; |
| @@ -542,9 +540,8 @@ static int bdrv_pread_em(BlockDriverState *bs, int64_t offset, | @@ -542,9 +540,8 @@ static int bdrv_pread_em(BlockDriverState *bs, int64_t offset, | ||
| 542 | } | 540 | } |
| 543 | 541 | ||
| 544 | static int bdrv_pwrite_em(BlockDriverState *bs, int64_t offset, | 542 | static int bdrv_pwrite_em(BlockDriverState *bs, int64_t offset, |
| 545 | - const void *buf1, int count1) | 543 | + const uint8_t *buf, int count1) |
| 546 | { | 544 | { |
| 547 | - const uint8_t *buf = buf1; | ||
| 548 | uint8_t tmp_buf[SECTOR_SIZE]; | 545 | uint8_t tmp_buf[SECTOR_SIZE]; |
| 549 | int len, nb_sectors, count; | 546 | int len, nb_sectors, count; |
| 550 | int64_t sector_num; | 547 | int64_t sector_num; |
| @@ -589,7 +586,6 @@ static int bdrv_pwrite_em(BlockDriverState *bs, int64_t offset, | @@ -589,7 +586,6 @@ static int bdrv_pwrite_em(BlockDriverState *bs, int64_t offset, | ||
| 589 | } | 586 | } |
| 590 | return count1; | 587 | return count1; |
| 591 | } | 588 | } |
| 592 | -#endif | ||
| 593 | 589 | ||
| 594 | /** | 590 | /** |
| 595 | * Read with byte offsets (needed only for file protocols) | 591 | * Read with byte offsets (needed only for file protocols) |
| @@ -602,7 +598,7 @@ int bdrv_pread(BlockDriverState *bs, int64_t offset, | @@ -602,7 +598,7 @@ int bdrv_pread(BlockDriverState *bs, int64_t offset, | ||
| 602 | if (!drv) | 598 | if (!drv) |
| 603 | return -ENOENT; | 599 | return -ENOENT; |
| 604 | if (!drv->bdrv_pread) | 600 | if (!drv->bdrv_pread) |
| 605 | - return -ENOTSUP; | 601 | + return bdrv_pread_em(bs, offset, buf1, count1); |
| 606 | return drv->bdrv_pread(bs, offset, buf1, count1); | 602 | return drv->bdrv_pread(bs, offset, buf1, count1); |
| 607 | } | 603 | } |
| 608 | 604 | ||
| @@ -617,7 +613,7 @@ int bdrv_pwrite(BlockDriverState *bs, int64_t offset, | @@ -617,7 +613,7 @@ int bdrv_pwrite(BlockDriverState *bs, int64_t offset, | ||
| 617 | if (!drv) | 613 | if (!drv) |
| 618 | return -ENOENT; | 614 | return -ENOENT; |
| 619 | if (!drv->bdrv_pwrite) | 615 | if (!drv->bdrv_pwrite) |
| 620 | - return -ENOTSUP; | 616 | + return bdrv_pwrite_em(bs, offset, buf1, count1); |
| 621 | return drv->bdrv_pwrite(bs, offset, buf1, count1); | 617 | return drv->bdrv_pwrite(bs, offset, buf1, count1); |
| 622 | } | 618 | } |
| 623 | 619 | ||
| @@ -859,6 +855,137 @@ void bdrv_get_backing_filename(BlockDriverState *bs, | @@ -859,6 +855,137 @@ void bdrv_get_backing_filename(BlockDriverState *bs, | ||
| 859 | } | 855 | } |
| 860 | } | 856 | } |
| 861 | 857 | ||
| 858 | +int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num, | ||
| 859 | + const uint8_t *buf, int nb_sectors) | ||
| 860 | +{ | ||
| 861 | + BlockDriver *drv = bs->drv; | ||
| 862 | + if (!drv) | ||
| 863 | + return -ENOENT; | ||
| 864 | + if (!drv->bdrv_write_compressed) | ||
| 865 | + return -ENOTSUP; | ||
| 866 | + return drv->bdrv_write_compressed(bs, sector_num, buf, nb_sectors); | ||
| 867 | +} | ||
| 868 | + | ||
| 869 | +int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) | ||
| 870 | +{ | ||
| 871 | + BlockDriver *drv = bs->drv; | ||
| 872 | + if (!drv) | ||
| 873 | + return -ENOENT; | ||
| 874 | + if (!drv->bdrv_get_info) | ||
| 875 | + return -ENOTSUP; | ||
| 876 | + memset(bdi, 0, sizeof(*bdi)); | ||
| 877 | + return drv->bdrv_get_info(bs, bdi); | ||
| 878 | +} | ||
| 879 | + | ||
| 880 | +/**************************************************************/ | ||
| 881 | +/* handling of snapshots */ | ||
| 882 | + | ||
| 883 | +int bdrv_snapshot_create(BlockDriverState *bs, | ||
| 884 | + QEMUSnapshotInfo *sn_info) | ||
| 885 | +{ | ||
| 886 | + BlockDriver *drv = bs->drv; | ||
| 887 | + if (!drv) | ||
| 888 | + return -ENOENT; | ||
| 889 | + if (!drv->bdrv_snapshot_create) | ||
| 890 | + return -ENOTSUP; | ||
| 891 | + return drv->bdrv_snapshot_create(bs, sn_info); | ||
| 892 | +} | ||
| 893 | + | ||
| 894 | +int bdrv_snapshot_goto(BlockDriverState *bs, | ||
| 895 | + const char *snapshot_id) | ||
| 896 | +{ | ||
| 897 | + BlockDriver *drv = bs->drv; | ||
| 898 | + if (!drv) | ||
| 899 | + return -ENOENT; | ||
| 900 | + if (!drv->bdrv_snapshot_goto) | ||
| 901 | + return -ENOTSUP; | ||
| 902 | + return drv->bdrv_snapshot_goto(bs, snapshot_id); | ||
| 903 | +} | ||
| 904 | + | ||
| 905 | +int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id) | ||
| 906 | +{ | ||
| 907 | + BlockDriver *drv = bs->drv; | ||
| 908 | + if (!drv) | ||
| 909 | + return -ENOENT; | ||
| 910 | + if (!drv->bdrv_snapshot_delete) | ||
| 911 | + return -ENOTSUP; | ||
| 912 | + return drv->bdrv_snapshot_delete(bs, snapshot_id); | ||
| 913 | +} | ||
| 914 | + | ||
| 915 | +int bdrv_snapshot_list(BlockDriverState *bs, | ||
| 916 | + QEMUSnapshotInfo **psn_info) | ||
| 917 | +{ | ||
| 918 | + BlockDriver *drv = bs->drv; | ||
| 919 | + if (!drv) | ||
| 920 | + return -ENOENT; | ||
| 921 | + if (!drv->bdrv_snapshot_list) | ||
| 922 | + return -ENOTSUP; | ||
| 923 | + return drv->bdrv_snapshot_list(bs, psn_info); | ||
| 924 | +} | ||
| 925 | + | ||
| 926 | +#define NB_SUFFIXES 4 | ||
| 927 | + | ||
| 928 | +char *get_human_readable_size(char *buf, int buf_size, int64_t size) | ||
| 929 | +{ | ||
| 930 | + static const char suffixes[NB_SUFFIXES] = "KMGT"; | ||
| 931 | + int64_t base; | ||
| 932 | + int i; | ||
| 933 | + | ||
| 934 | + if (size <= 999) { | ||
| 935 | + snprintf(buf, buf_size, "%" PRId64, size); | ||
| 936 | + } else { | ||
| 937 | + base = 1024; | ||
| 938 | + for(i = 0; i < NB_SUFFIXES; i++) { | ||
| 939 | + if (size < (10 * base)) { | ||
| 940 | + snprintf(buf, buf_size, "%0.1f%c", | ||
| 941 | + (double)size / base, | ||
| 942 | + suffixes[i]); | ||
| 943 | + break; | ||
| 944 | + } else if (size < (1000 * base) || i == (NB_SUFFIXES - 1)) { | ||
| 945 | + snprintf(buf, buf_size, "%" PRId64 "%c", | ||
| 946 | + ((size + (base >> 1)) / base), | ||
| 947 | + suffixes[i]); | ||
| 948 | + break; | ||
| 949 | + } | ||
| 950 | + base = base * 1024; | ||
| 951 | + } | ||
| 952 | + } | ||
| 953 | + return buf; | ||
| 954 | +} | ||
| 955 | + | ||
| 956 | +char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn) | ||
| 957 | +{ | ||
| 958 | + char buf1[128], date_buf[128], clock_buf[128]; | ||
| 959 | + struct tm tm; | ||
| 960 | + time_t ti; | ||
| 961 | + int64_t secs; | ||
| 962 | + | ||
| 963 | + if (!sn) { | ||
| 964 | + snprintf(buf, buf_size, | ||
| 965 | + "%-10s%-20s%7s%20s%15s", | ||
| 966 | + "ID", "TAG", "VM SIZE", "DATE", "VM CLOCK"); | ||
| 967 | + } else { | ||
| 968 | + ti = sn->date_sec; | ||
| 969 | + localtime_r(&ti, &tm); | ||
| 970 | + strftime(date_buf, sizeof(date_buf), | ||
| 971 | + "%Y-%m-%d %H:%M:%S", &tm); | ||
| 972 | + secs = sn->vm_clock_nsec / 1000000000; | ||
| 973 | + snprintf(clock_buf, sizeof(clock_buf), | ||
| 974 | + "%02d:%02d:%02d.%03d", | ||
| 975 | + (int)(secs / 3600), | ||
| 976 | + (int)((secs / 60) % 60), | ||
| 977 | + (int)(secs % 60), | ||
| 978 | + (int)((sn->vm_clock_nsec / 1000000) % 1000)); | ||
| 979 | + snprintf(buf, buf_size, | ||
| 980 | + "%-10s%-20s%7s%20s%15s", | ||
| 981 | + sn->id_str, sn->name, | ||
| 982 | + get_human_readable_size(buf1, sizeof(buf1), sn->vm_state_size), | ||
| 983 | + date_buf, | ||
| 984 | + clock_buf); | ||
| 985 | + } | ||
| 986 | + return buf; | ||
| 987 | +} | ||
| 988 | + | ||
| 862 | 989 | ||
| 863 | /**************************************************************/ | 990 | /**************************************************************/ |
| 864 | /* async I/Os */ | 991 | /* async I/Os */ |
| @@ -1108,4 +1235,5 @@ void bdrv_init(void) | @@ -1108,4 +1235,5 @@ void bdrv_init(void) | ||
| 1108 | bdrv_register(&bdrv_bochs); | 1235 | bdrv_register(&bdrv_bochs); |
| 1109 | bdrv_register(&bdrv_vpc); | 1236 | bdrv_register(&bdrv_vpc); |
| 1110 | bdrv_register(&bdrv_vvfat); | 1237 | bdrv_register(&bdrv_vvfat); |
| 1238 | + bdrv_register(&bdrv_qcow2); | ||
| 1111 | } | 1239 | } |
block_int.h
| @@ -57,6 +57,17 @@ struct BlockDriver { | @@ -57,6 +57,17 @@ struct BlockDriver { | ||
| 57 | const uint8_t *buf, int count); | 57 | const uint8_t *buf, int count); |
| 58 | int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset); | 58 | int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset); |
| 59 | int64_t (*bdrv_getlength)(BlockDriverState *bs); | 59 | int64_t (*bdrv_getlength)(BlockDriverState *bs); |
| 60 | + int (*bdrv_write_compressed)(BlockDriverState *bs, int64_t sector_num, | ||
| 61 | + const uint8_t *buf, int nb_sectors); | ||
| 62 | + | ||
| 63 | + int (*bdrv_snapshot_create)(BlockDriverState *bs, | ||
| 64 | + QEMUSnapshotInfo *sn_info); | ||
| 65 | + int (*bdrv_snapshot_goto)(BlockDriverState *bs, | ||
| 66 | + const char *snapshot_id); | ||
| 67 | + int (*bdrv_snapshot_delete)(BlockDriverState *bs, const char *snapshot_id); | ||
| 68 | + int (*bdrv_snapshot_list)(BlockDriverState *bs, | ||
| 69 | + QEMUSnapshotInfo **psn_info); | ||
| 70 | + int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi); | ||
| 60 | 71 | ||
| 61 | struct BlockDriver *next; | 72 | struct BlockDriver *next; |
| 62 | }; | 73 | }; |
monitor.c
| @@ -380,18 +380,6 @@ static void do_log(const char *items) | @@ -380,18 +380,6 @@ static void do_log(const char *items) | ||
| 380 | cpu_set_log(mask); | 380 | cpu_set_log(mask); |
| 381 | } | 381 | } |
| 382 | 382 | ||
| 383 | -static void do_savevm(const char *filename) | ||
| 384 | -{ | ||
| 385 | - if (qemu_savevm(filename) < 0) | ||
| 386 | - term_printf("I/O error when saving VM to '%s'\n", filename); | ||
| 387 | -} | ||
| 388 | - | ||
| 389 | -static void do_loadvm(const char *filename) | ||
| 390 | -{ | ||
| 391 | - if (qemu_loadvm(filename) < 0) | ||
| 392 | - term_printf("I/O error when loading VM from '%s'\n", filename); | ||
| 393 | -} | ||
| 394 | - | ||
| 395 | static void do_stop(void) | 383 | static void do_stop(void) |
| 396 | { | 384 | { |
| 397 | vm_stop(EXCP_INTERRUPT); | 385 | vm_stop(EXCP_INTERRUPT); |
| @@ -1155,10 +1143,12 @@ static term_cmd_t term_cmds[] = { | @@ -1155,10 +1143,12 @@ static term_cmd_t term_cmds[] = { | ||
| 1155 | "filename", "save screen into PPM image 'filename'" }, | 1143 | "filename", "save screen into PPM image 'filename'" }, |
| 1156 | { "log", "s", do_log, | 1144 | { "log", "s", do_log, |
| 1157 | "item1[,...]", "activate logging of the specified items to '/tmp/qemu.log'" }, | 1145 | "item1[,...]", "activate logging of the specified items to '/tmp/qemu.log'" }, |
| 1158 | - { "savevm", "F", do_savevm, | ||
| 1159 | - "filename", "save the whole virtual machine state to 'filename'" }, | ||
| 1160 | - { "loadvm", "F", do_loadvm, | ||
| 1161 | - "filename", "restore the whole virtual machine state from 'filename'" }, | 1146 | + { "savevm", "s?", do_savevm, |
| 1147 | + "tag|id", "save a VM snapshot. If no tag or id are provided, a new snapshot is created" }, | ||
| 1148 | + { "loadvm", "s", do_loadvm, | ||
| 1149 | + "tag|id", "restore a VM snapshot from its tag or id" }, | ||
| 1150 | + { "delvm", "s", do_delvm, | ||
| 1151 | + "tag|id", "delete a VM snapshot from its tag or id" }, | ||
| 1162 | { "stop", "", do_stop, | 1152 | { "stop", "", do_stop, |
| 1163 | "", "stop emulation", }, | 1153 | "", "stop emulation", }, |
| 1164 | { "c|cont", "", do_cont, | 1154 | { "c|cont", "", do_cont, |
| @@ -1241,6 +1231,8 @@ static term_cmd_t info_cmds[] = { | @@ -1241,6 +1231,8 @@ static term_cmd_t info_cmds[] = { | ||
| 1241 | "", "show profiling information", }, | 1231 | "", "show profiling information", }, |
| 1242 | { "capture", "", do_info_capture, | 1232 | { "capture", "", do_info_capture, |
| 1243 | "show capture information" }, | 1233 | "show capture information" }, |
| 1234 | + { "snapshots", "", do_info_snapshots, | ||
| 1235 | + "show the currently saved VM snapshots" }, | ||
| 1244 | { NULL, NULL, }, | 1236 | { NULL, NULL, }, |
| 1245 | }; | 1237 | }; |
| 1246 | 1238 |
qemu-img.c
| @@ -159,36 +159,6 @@ void help(void) | @@ -159,36 +159,6 @@ void help(void) | ||
| 159 | exit(1); | 159 | exit(1); |
| 160 | } | 160 | } |
| 161 | 161 | ||
| 162 | - | ||
| 163 | -#define NB_SUFFIXES 4 | ||
| 164 | - | ||
| 165 | -static void get_human_readable_size(char *buf, int buf_size, int64_t size) | ||
| 166 | -{ | ||
| 167 | - static const char suffixes[NB_SUFFIXES] = "KMGT"; | ||
| 168 | - int64_t base; | ||
| 169 | - int i; | ||
| 170 | - | ||
| 171 | - if (size <= 999) { | ||
| 172 | - snprintf(buf, buf_size, "%" PRId64, size); | ||
| 173 | - } else { | ||
| 174 | - base = 1024; | ||
| 175 | - for(i = 0; i < NB_SUFFIXES; i++) { | ||
| 176 | - if (size < (10 * base)) { | ||
| 177 | - snprintf(buf, buf_size, "%0.1f%c", | ||
| 178 | - (double)size / base, | ||
| 179 | - suffixes[i]); | ||
| 180 | - break; | ||
| 181 | - } else if (size < (1000 * base) || i == (NB_SUFFIXES - 1)) { | ||
| 182 | - snprintf(buf, buf_size, "%" PRId64 "%c", | ||
| 183 | - ((size + (base >> 1)) / base), | ||
| 184 | - suffixes[i]); | ||
| 185 | - break; | ||
| 186 | - } | ||
| 187 | - base = base * 1024; | ||
| 188 | - } | ||
| 189 | - } | ||
| 190 | -} | ||
| 191 | - | ||
| 192 | #if defined(WIN32) | 162 | #if defined(WIN32) |
| 193 | /* XXX: put correct support for win32 */ | 163 | /* XXX: put correct support for win32 */ |
| 194 | static int read_password(char *buf, int buf_size) | 164 | static int read_password(char *buf, int buf_size) |
| @@ -486,6 +456,7 @@ static int img_convert(int argc, char **argv) | @@ -486,6 +456,7 @@ static int img_convert(int argc, char **argv) | ||
| 486 | int64_t total_sectors, nb_sectors, sector_num; | 456 | int64_t total_sectors, nb_sectors, sector_num; |
| 487 | uint8_t buf[IO_BUF_SIZE]; | 457 | uint8_t buf[IO_BUF_SIZE]; |
| 488 | const uint8_t *buf1; | 458 | const uint8_t *buf1; |
| 459 | + BlockDriverInfo bdi; | ||
| 489 | 460 | ||
| 490 | fmt = NULL; | 461 | fmt = NULL; |
| 491 | out_fmt = "raw"; | 462 | out_fmt = "raw"; |
| @@ -525,9 +496,9 @@ static int img_convert(int argc, char **argv) | @@ -525,9 +496,9 @@ static int img_convert(int argc, char **argv) | ||
| 525 | drv = bdrv_find_format(out_fmt); | 496 | drv = bdrv_find_format(out_fmt); |
| 526 | if (!drv) | 497 | if (!drv) |
| 527 | error("Unknown file format '%s'", fmt); | 498 | error("Unknown file format '%s'", fmt); |
| 528 | - if (compress && drv != &bdrv_qcow) | 499 | + if (compress && drv != &bdrv_qcow && drv != &bdrv_qcow2) |
| 529 | error("Compression not supported for this file format"); | 500 | error("Compression not supported for this file format"); |
| 530 | - if (encrypt && drv != &bdrv_qcow) | 501 | + if (encrypt && drv != &bdrv_qcow && drv != &bdrv_qcow2) |
| 531 | error("Encryption not supported for this file format"); | 502 | error("Encryption not supported for this file format"); |
| 532 | if (compress && encrypt) | 503 | if (compress && encrypt) |
| 533 | error("Compression and encryption not supported at the same time"); | 504 | error("Compression and encryption not supported at the same time"); |
| @@ -544,7 +515,9 @@ static int img_convert(int argc, char **argv) | @@ -544,7 +515,9 @@ static int img_convert(int argc, char **argv) | ||
| 544 | out_bs = bdrv_new_open(out_filename, out_fmt); | 515 | out_bs = bdrv_new_open(out_filename, out_fmt); |
| 545 | 516 | ||
| 546 | if (compress) { | 517 | if (compress) { |
| 547 | - cluster_size = qcow_get_cluster_size(out_bs); | 518 | + if (bdrv_get_info(out_bs, &bdi) < 0) |
| 519 | + error("could not get block driver info"); | ||
| 520 | + cluster_size = bdi.cluster_size; | ||
| 548 | if (cluster_size <= 0 || cluster_size > IO_BUF_SIZE) | 521 | if (cluster_size <= 0 || cluster_size > IO_BUF_SIZE) |
| 549 | error("invalid cluster size"); | 522 | error("invalid cluster size"); |
| 550 | cluster_sectors = cluster_size >> 9; | 523 | cluster_sectors = cluster_size >> 9; |
| @@ -562,12 +535,15 @@ static int img_convert(int argc, char **argv) | @@ -562,12 +535,15 @@ static int img_convert(int argc, char **argv) | ||
| 562 | if (n < cluster_sectors) | 535 | if (n < cluster_sectors) |
| 563 | memset(buf + n * 512, 0, cluster_size - n * 512); | 536 | memset(buf + n * 512, 0, cluster_size - n * 512); |
| 564 | if (is_not_zero(buf, cluster_size)) { | 537 | if (is_not_zero(buf, cluster_size)) { |
| 565 | - if (qcow_compress_cluster(out_bs, sector_num, buf) != 0) | 538 | + if (bdrv_write_compressed(out_bs, sector_num, buf, |
| 539 | + cluster_sectors) != 0) | ||
| 566 | error("error while compressing sector %" PRId64, | 540 | error("error while compressing sector %" PRId64, |
| 567 | sector_num); | 541 | sector_num); |
| 568 | } | 542 | } |
| 569 | sector_num += n; | 543 | sector_num += n; |
| 570 | } | 544 | } |
| 545 | + /* signal EOF to align */ | ||
| 546 | + bdrv_write_compressed(out_bs, 0, NULL, 0); | ||
| 571 | } else { | 547 | } else { |
| 572 | sector_num = 0; | 548 | sector_num = 0; |
| 573 | for(;;) { | 549 | for(;;) { |
| @@ -630,6 +606,24 @@ static int64_t get_allocated_file_size(const char *filename) | @@ -630,6 +606,24 @@ static int64_t get_allocated_file_size(const char *filename) | ||
| 630 | } | 606 | } |
| 631 | #endif | 607 | #endif |
| 632 | 608 | ||
| 609 | +static void dump_snapshots(BlockDriverState *bs) | ||
| 610 | +{ | ||
| 611 | + QEMUSnapshotInfo *sn_tab, *sn; | ||
| 612 | + int nb_sns, i; | ||
| 613 | + char buf[256]; | ||
| 614 | + | ||
| 615 | + nb_sns = bdrv_snapshot_list(bs, &sn_tab); | ||
| 616 | + if (nb_sns <= 0) | ||
| 617 | + return; | ||
| 618 | + printf("Snapshot list:\n"); | ||
| 619 | + printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL)); | ||
| 620 | + for(i = 0; i < nb_sns; i++) { | ||
| 621 | + sn = &sn_tab[i]; | ||
| 622 | + printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn)); | ||
| 623 | + } | ||
| 624 | + qemu_free(sn_tab); | ||
| 625 | +} | ||
| 626 | + | ||
| 633 | static int img_info(int argc, char **argv) | 627 | static int img_info(int argc, char **argv) |
| 634 | { | 628 | { |
| 635 | int c; | 629 | int c; |
| @@ -640,6 +634,7 @@ static int img_info(int argc, char **argv) | @@ -640,6 +634,7 @@ static int img_info(int argc, char **argv) | ||
| 640 | int64_t total_sectors, allocated_size; | 634 | int64_t total_sectors, allocated_size; |
| 641 | char backing_filename[1024]; | 635 | char backing_filename[1024]; |
| 642 | char backing_filename2[1024]; | 636 | char backing_filename2[1024]; |
| 637 | + BlockDriverInfo bdi; | ||
| 643 | 638 | ||
| 644 | fmt = NULL; | 639 | fmt = NULL; |
| 645 | for(;;) { | 640 | for(;;) { |
| @@ -690,13 +685,19 @@ static int img_info(int argc, char **argv) | @@ -690,13 +685,19 @@ static int img_info(int argc, char **argv) | ||
| 690 | dsize_buf); | 685 | dsize_buf); |
| 691 | if (bdrv_is_encrypted(bs)) | 686 | if (bdrv_is_encrypted(bs)) |
| 692 | printf("encrypted: yes\n"); | 687 | printf("encrypted: yes\n"); |
| 688 | + if (bdrv_get_info(bs, &bdi) >= 0) { | ||
| 689 | + if (bdi.cluster_size != 0) | ||
| 690 | + printf("cluster_size: %d\n", bdi.cluster_size); | ||
| 691 | + } | ||
| 693 | bdrv_get_backing_filename(bs, backing_filename, sizeof(backing_filename)); | 692 | bdrv_get_backing_filename(bs, backing_filename, sizeof(backing_filename)); |
| 694 | - if (backing_filename[0] != '\0') | 693 | + if (backing_filename[0] != '\0') { |
| 695 | path_combine(backing_filename2, sizeof(backing_filename2), | 694 | path_combine(backing_filename2, sizeof(backing_filename2), |
| 696 | filename, backing_filename); | 695 | filename, backing_filename); |
| 697 | printf("backing file: %s (actual path: %s)\n", | 696 | printf("backing file: %s (actual path: %s)\n", |
| 698 | backing_filename, | 697 | backing_filename, |
| 699 | backing_filename2); | 698 | backing_filename2); |
| 699 | + } | ||
| 700 | + dump_snapshots(bs); | ||
| 700 | bdrv_delete(bs); | 701 | bdrv_delete(bs); |
| 701 | return 0; | 702 | return 0; |
| 702 | } | 703 | } |
vl.c
| @@ -113,7 +113,11 @@ char phys_ram_file[1024]; | @@ -113,7 +113,11 @@ char phys_ram_file[1024]; | ||
| 113 | void *ioport_opaque[MAX_IOPORTS]; | 113 | void *ioport_opaque[MAX_IOPORTS]; |
| 114 | IOPortReadFunc *ioport_read_table[3][MAX_IOPORTS]; | 114 | IOPortReadFunc *ioport_read_table[3][MAX_IOPORTS]; |
| 115 | IOPortWriteFunc *ioport_write_table[3][MAX_IOPORTS]; | 115 | IOPortWriteFunc *ioport_write_table[3][MAX_IOPORTS]; |
| 116 | -BlockDriverState *bs_table[MAX_DISKS], *fd_table[MAX_FD]; | 116 | +/* Note: bs_table[MAX_DISKS] is a dummy block driver if none available |
| 117 | + to store the VM snapshots */ | ||
| 118 | +BlockDriverState *bs_table[MAX_DISKS + 1], *fd_table[MAX_FD]; | ||
| 119 | +/* point to the block driver where the snapshots are managed */ | ||
| 120 | +BlockDriverState *bs_snapshots; | ||
| 117 | int vga_ram_size; | 121 | int vga_ram_size; |
| 118 | int bios_size; | 122 | int bios_size; |
| 119 | static DisplayState display_state; | 123 | static DisplayState display_state; |
| @@ -4085,14 +4089,190 @@ void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque) | @@ -4085,14 +4089,190 @@ void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque) | ||
| 4085 | /***********************************************************/ | 4089 | /***********************************************************/ |
| 4086 | /* savevm/loadvm support */ | 4090 | /* savevm/loadvm support */ |
| 4087 | 4091 | ||
| 4092 | +#define IO_BUF_SIZE 32768 | ||
| 4093 | + | ||
| 4094 | +struct QEMUFile { | ||
| 4095 | + FILE *outfile; | ||
| 4096 | + BlockDriverState *bs; | ||
| 4097 | + int is_file; | ||
| 4098 | + int is_writable; | ||
| 4099 | + int64_t base_offset; | ||
| 4100 | + int64_t buf_offset; /* start of buffer when writing, end of buffer | ||
| 4101 | + when reading */ | ||
| 4102 | + int buf_index; | ||
| 4103 | + int buf_size; /* 0 when writing */ | ||
| 4104 | + uint8_t buf[IO_BUF_SIZE]; | ||
| 4105 | +}; | ||
| 4106 | + | ||
| 4107 | +QEMUFile *qemu_fopen(const char *filename, const char *mode) | ||
| 4108 | +{ | ||
| 4109 | + QEMUFile *f; | ||
| 4110 | + | ||
| 4111 | + f = qemu_mallocz(sizeof(QEMUFile)); | ||
| 4112 | + if (!f) | ||
| 4113 | + return NULL; | ||
| 4114 | + if (!strcmp(mode, "wb")) { | ||
| 4115 | + f->is_writable = 1; | ||
| 4116 | + } else if (!strcmp(mode, "rb")) { | ||
| 4117 | + f->is_writable = 0; | ||
| 4118 | + } else { | ||
| 4119 | + goto fail; | ||
| 4120 | + } | ||
| 4121 | + f->outfile = fopen(filename, mode); | ||
| 4122 | + if (!f->outfile) | ||
| 4123 | + goto fail; | ||
| 4124 | + f->is_file = 1; | ||
| 4125 | + return f; | ||
| 4126 | + fail: | ||
| 4127 | + if (f->outfile) | ||
| 4128 | + fclose(f->outfile); | ||
| 4129 | + qemu_free(f); | ||
| 4130 | + return NULL; | ||
| 4131 | +} | ||
| 4132 | + | ||
| 4133 | +QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int64_t offset, int is_writable) | ||
| 4134 | +{ | ||
| 4135 | + QEMUFile *f; | ||
| 4136 | + | ||
| 4137 | + f = qemu_mallocz(sizeof(QEMUFile)); | ||
| 4138 | + if (!f) | ||
| 4139 | + return NULL; | ||
| 4140 | + f->is_file = 0; | ||
| 4141 | + f->bs = bs; | ||
| 4142 | + f->is_writable = is_writable; | ||
| 4143 | + f->base_offset = offset; | ||
| 4144 | + return f; | ||
| 4145 | +} | ||
| 4146 | + | ||
| 4147 | +void qemu_fflush(QEMUFile *f) | ||
| 4148 | +{ | ||
| 4149 | + if (!f->is_writable) | ||
| 4150 | + return; | ||
| 4151 | + if (f->buf_index > 0) { | ||
| 4152 | + if (f->is_file) { | ||
| 4153 | + fseek(f->outfile, f->buf_offset, SEEK_SET); | ||
| 4154 | + fwrite(f->buf, 1, f->buf_index, f->outfile); | ||
| 4155 | + } else { | ||
| 4156 | + bdrv_pwrite(f->bs, f->base_offset + f->buf_offset, | ||
| 4157 | + f->buf, f->buf_index); | ||
| 4158 | + } | ||
| 4159 | + f->buf_offset += f->buf_index; | ||
| 4160 | + f->buf_index = 0; | ||
| 4161 | + } | ||
| 4162 | +} | ||
| 4163 | + | ||
| 4164 | +static void qemu_fill_buffer(QEMUFile *f) | ||
| 4165 | +{ | ||
| 4166 | + int len; | ||
| 4167 | + | ||
| 4168 | + if (f->is_writable) | ||
| 4169 | + return; | ||
| 4170 | + if (f->is_file) { | ||
| 4171 | + fseek(f->outfile, f->buf_offset, SEEK_SET); | ||
| 4172 | + len = fread(f->buf, 1, IO_BUF_SIZE, f->outfile); | ||
| 4173 | + if (len < 0) | ||
| 4174 | + len = 0; | ||
| 4175 | + } else { | ||
| 4176 | + len = bdrv_pread(f->bs, f->base_offset + f->buf_offset, | ||
| 4177 | + f->buf, IO_BUF_SIZE); | ||
| 4178 | + if (len < 0) | ||
| 4179 | + len = 0; | ||
| 4180 | + } | ||
| 4181 | + f->buf_index = 0; | ||
| 4182 | + f->buf_size = len; | ||
| 4183 | + f->buf_offset += len; | ||
| 4184 | +} | ||
| 4185 | + | ||
| 4186 | +void qemu_fclose(QEMUFile *f) | ||
| 4187 | +{ | ||
| 4188 | + if (f->is_writable) | ||
| 4189 | + qemu_fflush(f); | ||
| 4190 | + if (f->is_file) { | ||
| 4191 | + fclose(f->outfile); | ||
| 4192 | + } | ||
| 4193 | + qemu_free(f); | ||
| 4194 | +} | ||
| 4195 | + | ||
| 4088 | void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size) | 4196 | void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size) |
| 4089 | { | 4197 | { |
| 4090 | - fwrite(buf, 1, size, f); | 4198 | + int l; |
| 4199 | + while (size > 0) { | ||
| 4200 | + l = IO_BUF_SIZE - f->buf_index; | ||
| 4201 | + if (l > size) | ||
| 4202 | + l = size; | ||
| 4203 | + memcpy(f->buf + f->buf_index, buf, l); | ||
| 4204 | + f->buf_index += l; | ||
| 4205 | + buf += l; | ||
| 4206 | + size -= l; | ||
| 4207 | + if (f->buf_index >= IO_BUF_SIZE) | ||
| 4208 | + qemu_fflush(f); | ||
| 4209 | + } | ||
| 4091 | } | 4210 | } |
| 4092 | 4211 | ||
| 4093 | void qemu_put_byte(QEMUFile *f, int v) | 4212 | void qemu_put_byte(QEMUFile *f, int v) |
| 4094 | { | 4213 | { |
| 4095 | - fputc(v, f); | 4214 | + f->buf[f->buf_index++] = v; |
| 4215 | + if (f->buf_index >= IO_BUF_SIZE) | ||
| 4216 | + qemu_fflush(f); | ||
| 4217 | +} | ||
| 4218 | + | ||
| 4219 | +int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size1) | ||
| 4220 | +{ | ||
| 4221 | + int size, l; | ||
| 4222 | + | ||
| 4223 | + size = size1; | ||
| 4224 | + while (size > 0) { | ||
| 4225 | + l = f->buf_size - f->buf_index; | ||
| 4226 | + if (l == 0) { | ||
| 4227 | + qemu_fill_buffer(f); | ||
| 4228 | + l = f->buf_size - f->buf_index; | ||
| 4229 | + if (l == 0) | ||
| 4230 | + break; | ||
| 4231 | + } | ||
| 4232 | + if (l > size) | ||
| 4233 | + l = size; | ||
| 4234 | + memcpy(buf, f->buf + f->buf_index, l); | ||
| 4235 | + f->buf_index += l; | ||
| 4236 | + buf += l; | ||
| 4237 | + size -= l; | ||
| 4238 | + } | ||
| 4239 | + return size1 - size; | ||
| 4240 | +} | ||
| 4241 | + | ||
| 4242 | +int qemu_get_byte(QEMUFile *f) | ||
| 4243 | +{ | ||
| 4244 | + if (f->buf_index >= f->buf_size) { | ||
| 4245 | + qemu_fill_buffer(f); | ||
| 4246 | + if (f->buf_index >= f->buf_size) | ||
| 4247 | + return 0; | ||
| 4248 | + } | ||
| 4249 | + return f->buf[f->buf_index++]; | ||
| 4250 | +} | ||
| 4251 | + | ||
| 4252 | +int64_t qemu_ftell(QEMUFile *f) | ||
| 4253 | +{ | ||
| 4254 | + return f->buf_offset - f->buf_size + f->buf_index; | ||
| 4255 | +} | ||
| 4256 | + | ||
| 4257 | +int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence) | ||
| 4258 | +{ | ||
| 4259 | + if (whence == SEEK_SET) { | ||
| 4260 | + /* nothing to do */ | ||
| 4261 | + } else if (whence == SEEK_CUR) { | ||
| 4262 | + pos += qemu_ftell(f); | ||
| 4263 | + } else { | ||
| 4264 | + /* SEEK_END not supported */ | ||
| 4265 | + return -1; | ||
| 4266 | + } | ||
| 4267 | + if (f->is_writable) { | ||
| 4268 | + qemu_fflush(f); | ||
| 4269 | + f->buf_offset = pos; | ||
| 4270 | + } else { | ||
| 4271 | + f->buf_offset = pos; | ||
| 4272 | + f->buf_index = 0; | ||
| 4273 | + f->buf_size = 0; | ||
| 4274 | + } | ||
| 4275 | + return pos; | ||
| 4096 | } | 4276 | } |
| 4097 | 4277 | ||
| 4098 | void qemu_put_be16(QEMUFile *f, unsigned int v) | 4278 | void qemu_put_be16(QEMUFile *f, unsigned int v) |
| @@ -4115,21 +4295,6 @@ void qemu_put_be64(QEMUFile *f, uint64_t v) | @@ -4115,21 +4295,6 @@ void qemu_put_be64(QEMUFile *f, uint64_t v) | ||
| 4115 | qemu_put_be32(f, v); | 4295 | qemu_put_be32(f, v); |
| 4116 | } | 4296 | } |
| 4117 | 4297 | ||
| 4118 | -int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size) | ||
| 4119 | -{ | ||
| 4120 | - return fread(buf, 1, size, f); | ||
| 4121 | -} | ||
| 4122 | - | ||
| 4123 | -int qemu_get_byte(QEMUFile *f) | ||
| 4124 | -{ | ||
| 4125 | - int v; | ||
| 4126 | - v = fgetc(f); | ||
| 4127 | - if (v == EOF) | ||
| 4128 | - return 0; | ||
| 4129 | - else | ||
| 4130 | - return v; | ||
| 4131 | -} | ||
| 4132 | - | ||
| 4133 | unsigned int qemu_get_be16(QEMUFile *f) | 4298 | unsigned int qemu_get_be16(QEMUFile *f) |
| 4134 | { | 4299 | { |
| 4135 | unsigned int v; | 4300 | unsigned int v; |
| @@ -4156,18 +4321,6 @@ uint64_t qemu_get_be64(QEMUFile *f) | @@ -4156,18 +4321,6 @@ uint64_t qemu_get_be64(QEMUFile *f) | ||
| 4156 | return v; | 4321 | return v; |
| 4157 | } | 4322 | } |
| 4158 | 4323 | ||
| 4159 | -int64_t qemu_ftell(QEMUFile *f) | ||
| 4160 | -{ | ||
| 4161 | - return ftell(f); | ||
| 4162 | -} | ||
| 4163 | - | ||
| 4164 | -int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence) | ||
| 4165 | -{ | ||
| 4166 | - if (fseek(f, pos, whence) < 0) | ||
| 4167 | - return -1; | ||
| 4168 | - return ftell(f); | ||
| 4169 | -} | ||
| 4170 | - | ||
| 4171 | typedef struct SaveStateEntry { | 4324 | typedef struct SaveStateEntry { |
| 4172 | char idstr[256]; | 4325 | char idstr[256]; |
| 4173 | int instance_id; | 4326 | int instance_id; |
| @@ -4209,25 +4362,18 @@ int register_savevm(const char *idstr, | @@ -4209,25 +4362,18 @@ int register_savevm(const char *idstr, | ||
| 4209 | } | 4362 | } |
| 4210 | 4363 | ||
| 4211 | #define QEMU_VM_FILE_MAGIC 0x5145564d | 4364 | #define QEMU_VM_FILE_MAGIC 0x5145564d |
| 4212 | -#define QEMU_VM_FILE_VERSION 0x00000001 | 4365 | +#define QEMU_VM_FILE_VERSION 0x00000002 |
| 4213 | 4366 | ||
| 4214 | -int qemu_savevm(const char *filename) | 4367 | +int qemu_savevm_state(QEMUFile *f) |
| 4215 | { | 4368 | { |
| 4216 | SaveStateEntry *se; | 4369 | SaveStateEntry *se; |
| 4217 | - QEMUFile *f; | ||
| 4218 | - int len, len_pos, cur_pos, saved_vm_running, ret; | ||
| 4219 | - | ||
| 4220 | - saved_vm_running = vm_running; | ||
| 4221 | - vm_stop(0); | ||
| 4222 | - | ||
| 4223 | - f = fopen(filename, "wb"); | ||
| 4224 | - if (!f) { | ||
| 4225 | - ret = -1; | ||
| 4226 | - goto the_end; | ||
| 4227 | - } | 4370 | + int len, ret; |
| 4371 | + int64_t cur_pos, len_pos, total_len_pos; | ||
| 4228 | 4372 | ||
| 4229 | qemu_put_be32(f, QEMU_VM_FILE_MAGIC); | 4373 | qemu_put_be32(f, QEMU_VM_FILE_MAGIC); |
| 4230 | qemu_put_be32(f, QEMU_VM_FILE_VERSION); | 4374 | qemu_put_be32(f, QEMU_VM_FILE_VERSION); |
| 4375 | + total_len_pos = qemu_ftell(f); | ||
| 4376 | + qemu_put_be64(f, 0); /* total size */ | ||
| 4231 | 4377 | ||
| 4232 | for(se = first_se; se != NULL; se = se->next) { | 4378 | for(se = first_se; se != NULL; se = se->next) { |
| 4233 | /* ID string */ | 4379 | /* ID string */ |
| @@ -4239,24 +4385,24 @@ int qemu_savevm(const char *filename) | @@ -4239,24 +4385,24 @@ int qemu_savevm(const char *filename) | ||
| 4239 | qemu_put_be32(f, se->version_id); | 4385 | qemu_put_be32(f, se->version_id); |
| 4240 | 4386 | ||
| 4241 | /* record size: filled later */ | 4387 | /* record size: filled later */ |
| 4242 | - len_pos = ftell(f); | 4388 | + len_pos = qemu_ftell(f); |
| 4243 | qemu_put_be32(f, 0); | 4389 | qemu_put_be32(f, 0); |
| 4244 | 4390 | ||
| 4245 | se->save_state(f, se->opaque); | 4391 | se->save_state(f, se->opaque); |
| 4246 | 4392 | ||
| 4247 | /* fill record size */ | 4393 | /* fill record size */ |
| 4248 | - cur_pos = ftell(f); | ||
| 4249 | - len = ftell(f) - len_pos - 4; | ||
| 4250 | - fseek(f, len_pos, SEEK_SET); | 4394 | + cur_pos = qemu_ftell(f); |
| 4395 | + len = cur_pos - len_pos - 4; | ||
| 4396 | + qemu_fseek(f, len_pos, SEEK_SET); | ||
| 4251 | qemu_put_be32(f, len); | 4397 | qemu_put_be32(f, len); |
| 4252 | - fseek(f, cur_pos, SEEK_SET); | 4398 | + qemu_fseek(f, cur_pos, SEEK_SET); |
| 4253 | } | 4399 | } |
| 4400 | + cur_pos = qemu_ftell(f); | ||
| 4401 | + qemu_fseek(f, total_len_pos, SEEK_SET); | ||
| 4402 | + qemu_put_be64(f, cur_pos - total_len_pos - 8); | ||
| 4403 | + qemu_fseek(f, cur_pos, SEEK_SET); | ||
| 4254 | 4404 | ||
| 4255 | - fclose(f); | ||
| 4256 | ret = 0; | 4405 | ret = 0; |
| 4257 | - the_end: | ||
| 4258 | - if (saved_vm_running) | ||
| 4259 | - vm_start(); | ||
| 4260 | return ret; | 4406 | return ret; |
| 4261 | } | 4407 | } |
| 4262 | 4408 | ||
| @@ -4272,38 +4418,29 @@ static SaveStateEntry *find_se(const char *idstr, int instance_id) | @@ -4272,38 +4418,29 @@ static SaveStateEntry *find_se(const char *idstr, int instance_id) | ||
| 4272 | return NULL; | 4418 | return NULL; |
| 4273 | } | 4419 | } |
| 4274 | 4420 | ||
| 4275 | -int qemu_loadvm(const char *filename) | 4421 | +int qemu_loadvm_state(QEMUFile *f) |
| 4276 | { | 4422 | { |
| 4277 | SaveStateEntry *se; | 4423 | SaveStateEntry *se; |
| 4278 | - QEMUFile *f; | ||
| 4279 | - int len, cur_pos, ret, instance_id, record_len, version_id; | ||
| 4280 | - int saved_vm_running; | 4424 | + int len, ret, instance_id, record_len, version_id; |
| 4425 | + int64_t total_len, end_pos, cur_pos; | ||
| 4281 | unsigned int v; | 4426 | unsigned int v; |
| 4282 | char idstr[256]; | 4427 | char idstr[256]; |
| 4283 | 4428 | ||
| 4284 | - saved_vm_running = vm_running; | ||
| 4285 | - vm_stop(0); | ||
| 4286 | - | ||
| 4287 | - f = fopen(filename, "rb"); | ||
| 4288 | - if (!f) { | ||
| 4289 | - ret = -1; | ||
| 4290 | - goto the_end; | ||
| 4291 | - } | ||
| 4292 | - | ||
| 4293 | v = qemu_get_be32(f); | 4429 | v = qemu_get_be32(f); |
| 4294 | if (v != QEMU_VM_FILE_MAGIC) | 4430 | if (v != QEMU_VM_FILE_MAGIC) |
| 4295 | goto fail; | 4431 | goto fail; |
| 4296 | v = qemu_get_be32(f); | 4432 | v = qemu_get_be32(f); |
| 4297 | if (v != QEMU_VM_FILE_VERSION) { | 4433 | if (v != QEMU_VM_FILE_VERSION) { |
| 4298 | fail: | 4434 | fail: |
| 4299 | - fclose(f); | ||
| 4300 | ret = -1; | 4435 | ret = -1; |
| 4301 | goto the_end; | 4436 | goto the_end; |
| 4302 | } | 4437 | } |
| 4438 | + total_len = qemu_get_be64(f); | ||
| 4439 | + end_pos = total_len + qemu_ftell(f); | ||
| 4303 | for(;;) { | 4440 | for(;;) { |
| 4304 | - len = qemu_get_byte(f); | ||
| 4305 | - if (feof(f)) | 4441 | + if (qemu_ftell(f) >= end_pos) |
| 4306 | break; | 4442 | break; |
| 4443 | + len = qemu_get_byte(f); | ||
| 4307 | qemu_get_buffer(f, idstr, len); | 4444 | qemu_get_buffer(f, idstr, len); |
| 4308 | idstr[len] = '\0'; | 4445 | idstr[len] = '\0'; |
| 4309 | instance_id = qemu_get_be32(f); | 4446 | instance_id = qemu_get_be32(f); |
| @@ -4313,7 +4450,7 @@ int qemu_loadvm(const char *filename) | @@ -4313,7 +4450,7 @@ int qemu_loadvm(const char *filename) | ||
| 4313 | printf("idstr=%s instance=0x%x version=%d len=%d\n", | 4450 | printf("idstr=%s instance=0x%x version=%d len=%d\n", |
| 4314 | idstr, instance_id, version_id, record_len); | 4451 | idstr, instance_id, version_id, record_len); |
| 4315 | #endif | 4452 | #endif |
| 4316 | - cur_pos = ftell(f); | 4453 | + cur_pos = qemu_ftell(f); |
| 4317 | se = find_se(idstr, instance_id); | 4454 | se = find_se(idstr, instance_id); |
| 4318 | if (!se) { | 4455 | if (!se) { |
| 4319 | fprintf(stderr, "qemu: warning: instance 0x%x of device '%s' not present in current VM\n", | 4456 | fprintf(stderr, "qemu: warning: instance 0x%x of device '%s' not present in current VM\n", |
| @@ -4328,12 +4465,281 @@ int qemu_loadvm(const char *filename) | @@ -4328,12 +4465,281 @@ int qemu_loadvm(const char *filename) | ||
| 4328 | /* always seek to exact end of record */ | 4465 | /* always seek to exact end of record */ |
| 4329 | qemu_fseek(f, cur_pos + record_len, SEEK_SET); | 4466 | qemu_fseek(f, cur_pos + record_len, SEEK_SET); |
| 4330 | } | 4467 | } |
| 4331 | - fclose(f); | ||
| 4332 | ret = 0; | 4468 | ret = 0; |
| 4333 | the_end: | 4469 | the_end: |
| 4470 | + return ret; | ||
| 4471 | +} | ||
| 4472 | + | ||
| 4473 | +/* device can contain snapshots */ | ||
| 4474 | +static int bdrv_can_snapshot(BlockDriverState *bs) | ||
| 4475 | +{ | ||
| 4476 | + return (bs && | ||
| 4477 | + !bdrv_is_removable(bs) && | ||
| 4478 | + !bdrv_is_read_only(bs)); | ||
| 4479 | +} | ||
| 4480 | + | ||
| 4481 | +/* device must be snapshots in order to have a reliable snapshot */ | ||
| 4482 | +static int bdrv_has_snapshot(BlockDriverState *bs) | ||
| 4483 | +{ | ||
| 4484 | + return (bs && | ||
| 4485 | + !bdrv_is_removable(bs) && | ||
| 4486 | + !bdrv_is_read_only(bs)); | ||
| 4487 | +} | ||
| 4488 | + | ||
| 4489 | +static BlockDriverState *get_bs_snapshots(void) | ||
| 4490 | +{ | ||
| 4491 | + BlockDriverState *bs; | ||
| 4492 | + int i; | ||
| 4493 | + | ||
| 4494 | + if (bs_snapshots) | ||
| 4495 | + return bs_snapshots; | ||
| 4496 | + for(i = 0; i <= MAX_DISKS; i++) { | ||
| 4497 | + bs = bs_table[i]; | ||
| 4498 | + if (bdrv_can_snapshot(bs)) | ||
| 4499 | + goto ok; | ||
| 4500 | + } | ||
| 4501 | + return NULL; | ||
| 4502 | + ok: | ||
| 4503 | + bs_snapshots = bs; | ||
| 4504 | + return bs; | ||
| 4505 | +} | ||
| 4506 | + | ||
| 4507 | +static int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, | ||
| 4508 | + const char *name) | ||
| 4509 | +{ | ||
| 4510 | + QEMUSnapshotInfo *sn_tab, *sn; | ||
| 4511 | + int nb_sns, i, ret; | ||
| 4512 | + | ||
| 4513 | + ret = -ENOENT; | ||
| 4514 | + nb_sns = bdrv_snapshot_list(bs, &sn_tab); | ||
| 4515 | + if (nb_sns < 0) | ||
| 4516 | + return ret; | ||
| 4517 | + for(i = 0; i < nb_sns; i++) { | ||
| 4518 | + sn = &sn_tab[i]; | ||
| 4519 | + if (!strcmp(sn->id_str, name) || !strcmp(sn->name, name)) { | ||
| 4520 | + *sn_info = *sn; | ||
| 4521 | + ret = 0; | ||
| 4522 | + break; | ||
| 4523 | + } | ||
| 4524 | + } | ||
| 4525 | + qemu_free(sn_tab); | ||
| 4526 | + return ret; | ||
| 4527 | +} | ||
| 4528 | + | ||
| 4529 | +void do_savevm(const char *name) | ||
| 4530 | +{ | ||
| 4531 | + BlockDriverState *bs, *bs1; | ||
| 4532 | + QEMUSnapshotInfo sn1, *sn = &sn1, old_sn1, *old_sn = &old_sn1; | ||
| 4533 | + int must_delete, ret, i; | ||
| 4534 | + BlockDriverInfo bdi1, *bdi = &bdi1; | ||
| 4535 | + QEMUFile *f; | ||
| 4536 | + int saved_vm_running; | ||
| 4537 | + struct timeval tv; | ||
| 4538 | + | ||
| 4539 | + bs = get_bs_snapshots(); | ||
| 4540 | + if (!bs) { | ||
| 4541 | + term_printf("No block device can accept snapshots\n"); | ||
| 4542 | + return; | ||
| 4543 | + } | ||
| 4544 | + | ||
| 4545 | + saved_vm_running = vm_running; | ||
| 4546 | + vm_stop(0); | ||
| 4547 | + | ||
| 4548 | + must_delete = 0; | ||
| 4549 | + if (name) { | ||
| 4550 | + ret = bdrv_snapshot_find(bs, old_sn, name); | ||
| 4551 | + if (ret >= 0) { | ||
| 4552 | + must_delete = 1; | ||
| 4553 | + } | ||
| 4554 | + } | ||
| 4555 | + memset(sn, 0, sizeof(*sn)); | ||
| 4556 | + if (must_delete) { | ||
| 4557 | + pstrcpy(sn->name, sizeof(sn->name), old_sn->name); | ||
| 4558 | + pstrcpy(sn->id_str, sizeof(sn->id_str), old_sn->id_str); | ||
| 4559 | + } else { | ||
| 4560 | + if (name) | ||
| 4561 | + pstrcpy(sn->name, sizeof(sn->name), name); | ||
| 4562 | + } | ||
| 4563 | + | ||
| 4564 | + /* fill auxiliary fields */ | ||
| 4565 | + gettimeofday(&tv, NULL); | ||
| 4566 | + sn->date_sec = tv.tv_sec; | ||
| 4567 | + sn->date_nsec = tv.tv_usec * 1000; | ||
| 4568 | + sn->vm_clock_nsec = qemu_get_clock(vm_clock); | ||
| 4569 | + | ||
| 4570 | + if (bdrv_get_info(bs, bdi) < 0 || bdi->vm_state_offset <= 0) { | ||
| 4571 | + term_printf("Device %s does not support VM state snapshots\n", | ||
| 4572 | + bdrv_get_device_name(bs)); | ||
| 4573 | + goto the_end; | ||
| 4574 | + } | ||
| 4575 | + | ||
| 4576 | + /* save the VM state */ | ||
| 4577 | + f = qemu_fopen_bdrv(bs, bdi->vm_state_offset, 1); | ||
| 4578 | + if (!f) { | ||
| 4579 | + term_printf("Could not open VM state file\n"); | ||
| 4580 | + goto the_end; | ||
| 4581 | + } | ||
| 4582 | + ret = qemu_savevm_state(f); | ||
| 4583 | + sn->vm_state_size = qemu_ftell(f); | ||
| 4584 | + qemu_fclose(f); | ||
| 4585 | + if (ret < 0) { | ||
| 4586 | + term_printf("Error %d while writing VM\n", ret); | ||
| 4587 | + goto the_end; | ||
| 4588 | + } | ||
| 4589 | + | ||
| 4590 | + /* create the snapshots */ | ||
| 4591 | + | ||
| 4592 | + for(i = 0; i < MAX_DISKS; i++) { | ||
| 4593 | + bs1 = bs_table[i]; | ||
| 4594 | + if (bdrv_has_snapshot(bs1)) { | ||
| 4595 | + if (must_delete) { | ||
| 4596 | + ret = bdrv_snapshot_delete(bs1, old_sn->id_str); | ||
| 4597 | + if (ret < 0) { | ||
| 4598 | + term_printf("Error while deleting snapshot on '%s'\n", | ||
| 4599 | + bdrv_get_device_name(bs1)); | ||
| 4600 | + } | ||
| 4601 | + } | ||
| 4602 | + ret = bdrv_snapshot_create(bs1, sn); | ||
| 4603 | + if (ret < 0) { | ||
| 4604 | + term_printf("Error while creating snapshot on '%s'\n", | ||
| 4605 | + bdrv_get_device_name(bs1)); | ||
| 4606 | + } | ||
| 4607 | + } | ||
| 4608 | + } | ||
| 4609 | + | ||
| 4610 | + the_end: | ||
| 4334 | if (saved_vm_running) | 4611 | if (saved_vm_running) |
| 4335 | vm_start(); | 4612 | vm_start(); |
| 4336 | - return ret; | 4613 | +} |
| 4614 | + | ||
| 4615 | +void do_loadvm(const char *name) | ||
| 4616 | +{ | ||
| 4617 | + BlockDriverState *bs, *bs1; | ||
| 4618 | + BlockDriverInfo bdi1, *bdi = &bdi1; | ||
| 4619 | + QEMUFile *f; | ||
| 4620 | + int i, ret; | ||
| 4621 | + int saved_vm_running; | ||
| 4622 | + | ||
| 4623 | + bs = get_bs_snapshots(); | ||
| 4624 | + if (!bs) { | ||
| 4625 | + term_printf("No block device supports snapshots\n"); | ||
| 4626 | + return; | ||
| 4627 | + } | ||
| 4628 | + | ||
| 4629 | + saved_vm_running = vm_running; | ||
| 4630 | + vm_stop(0); | ||
| 4631 | + | ||
| 4632 | + for(i = 0; i <= MAX_DISKS; i++) { | ||
| 4633 | + bs1 = bs_table[i]; | ||
| 4634 | + if (bdrv_has_snapshot(bs1)) { | ||
| 4635 | + ret = bdrv_snapshot_goto(bs1, name); | ||
| 4636 | + if (ret < 0) { | ||
| 4637 | + if (bs != bs1) | ||
| 4638 | + term_printf("Warning: "); | ||
| 4639 | + switch(ret) { | ||
| 4640 | + case -ENOTSUP: | ||
| 4641 | + term_printf("Snapshots not supported on device '%s'\n", | ||
| 4642 | + bdrv_get_device_name(bs1)); | ||
| 4643 | + break; | ||
| 4644 | + case -ENOENT: | ||
| 4645 | + term_printf("Could not find snapshot '%s' on device '%s'\n", | ||
| 4646 | + name, bdrv_get_device_name(bs1)); | ||
| 4647 | + break; | ||
| 4648 | + default: | ||
| 4649 | + term_printf("Error %d while activating snapshot on '%s'\n", | ||
| 4650 | + ret, bdrv_get_device_name(bs1)); | ||
| 4651 | + break; | ||
| 4652 | + } | ||
| 4653 | + /* fatal on snapshot block device */ | ||
| 4654 | + if (bs == bs1) | ||
| 4655 | + goto the_end; | ||
| 4656 | + } | ||
| 4657 | + } | ||
| 4658 | + } | ||
| 4659 | + | ||
| 4660 | + if (bdrv_get_info(bs, bdi) < 0 || bdi->vm_state_offset <= 0) { | ||
| 4661 | + term_printf("Device %s does not support VM state snapshots\n", | ||
| 4662 | + bdrv_get_device_name(bs)); | ||
| 4663 | + return; | ||
| 4664 | + } | ||
| 4665 | + | ||
| 4666 | + /* restore the VM state */ | ||
| 4667 | + f = qemu_fopen_bdrv(bs, bdi->vm_state_offset, 0); | ||
| 4668 | + if (!f) { | ||
| 4669 | + term_printf("Could not open VM state file\n"); | ||
| 4670 | + goto the_end; | ||
| 4671 | + } | ||
| 4672 | + ret = qemu_loadvm_state(f); | ||
| 4673 | + qemu_fclose(f); | ||
| 4674 | + if (ret < 0) { | ||
| 4675 | + term_printf("Error %d while loading VM state\n", ret); | ||
| 4676 | + } | ||
| 4677 | + the_end: | ||
| 4678 | + if (saved_vm_running) | ||
| 4679 | + vm_start(); | ||
| 4680 | +} | ||
| 4681 | + | ||
| 4682 | +void do_delvm(const char *name) | ||
| 4683 | +{ | ||
| 4684 | + BlockDriverState *bs, *bs1; | ||
| 4685 | + int i, ret; | ||
| 4686 | + | ||
| 4687 | + bs = get_bs_snapshots(); | ||
| 4688 | + if (!bs) { | ||
| 4689 | + term_printf("No block device supports snapshots\n"); | ||
| 4690 | + return; | ||
| 4691 | + } | ||
| 4692 | + | ||
| 4693 | + for(i = 0; i <= MAX_DISKS; i++) { | ||
| 4694 | + bs1 = bs_table[i]; | ||
| 4695 | + if (bdrv_has_snapshot(bs1)) { | ||
| 4696 | + ret = bdrv_snapshot_delete(bs1, name); | ||
| 4697 | + if (ret < 0) { | ||
| 4698 | + if (ret == -ENOTSUP) | ||
| 4699 | + term_printf("Snapshots not supported on device '%s'\n", | ||
| 4700 | + bdrv_get_device_name(bs1)); | ||
| 4701 | + else | ||
| 4702 | + term_printf("Error %d while deleting snapshot on '%s'\n", | ||
| 4703 | + ret, bdrv_get_device_name(bs1)); | ||
| 4704 | + } | ||
| 4705 | + } | ||
| 4706 | + } | ||
| 4707 | +} | ||
| 4708 | + | ||
| 4709 | +void do_info_snapshots(void) | ||
| 4710 | +{ | ||
| 4711 | + BlockDriverState *bs, *bs1; | ||
| 4712 | + QEMUSnapshotInfo *sn_tab, *sn; | ||
| 4713 | + int nb_sns, i; | ||
| 4714 | + char buf[256]; | ||
| 4715 | + | ||
| 4716 | + bs = get_bs_snapshots(); | ||
| 4717 | + if (!bs) { | ||
| 4718 | + term_printf("No available block device supports snapshots\n"); | ||
| 4719 | + return; | ||
| 4720 | + } | ||
| 4721 | + term_printf("Snapshot devices:"); | ||
| 4722 | + for(i = 0; i <= MAX_DISKS; i++) { | ||
| 4723 | + bs1 = bs_table[i]; | ||
| 4724 | + if (bdrv_has_snapshot(bs1)) { | ||
| 4725 | + if (bs == bs1) | ||
| 4726 | + term_printf(" %s", bdrv_get_device_name(bs1)); | ||
| 4727 | + } | ||
| 4728 | + } | ||
| 4729 | + term_printf("\n"); | ||
| 4730 | + | ||
| 4731 | + nb_sns = bdrv_snapshot_list(bs, &sn_tab); | ||
| 4732 | + if (nb_sns < 0) { | ||
| 4733 | + term_printf("bdrv_snapshot_list: error %d\n", nb_sns); | ||
| 4734 | + return; | ||
| 4735 | + } | ||
| 4736 | + term_printf("Snapshot list (from %s):\n", bdrv_get_device_name(bs)); | ||
| 4737 | + term_printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL)); | ||
| 4738 | + for(i = 0; i < nb_sns; i++) { | ||
| 4739 | + sn = &sn_tab[i]; | ||
| 4740 | + term_printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn)); | ||
| 4741 | + } | ||
| 4742 | + qemu_free(sn_tab); | ||
| 4337 | } | 4743 | } |
| 4338 | 4744 | ||
| 4339 | /***********************************************************/ | 4745 | /***********************************************************/ |
| @@ -6284,7 +6690,7 @@ int main(int argc, char **argv) | @@ -6284,7 +6690,7 @@ int main(int argc, char **argv) | ||
| 6284 | } else | 6690 | } else |
| 6285 | #endif | 6691 | #endif |
| 6286 | if (loadvm) | 6692 | if (loadvm) |
| 6287 | - qemu_loadvm(loadvm); | 6693 | + do_loadvm(loadvm); |
| 6288 | 6694 | ||
| 6289 | { | 6695 | { |
| 6290 | /* XXX: simplify init */ | 6696 | /* XXX: simplify init */ |
vl.h
| @@ -396,8 +396,11 @@ void cpu_disable_ticks(void); | @@ -396,8 +396,11 @@ void cpu_disable_ticks(void); | ||
| 396 | 396 | ||
| 397 | /* VM Load/Save */ | 397 | /* VM Load/Save */ |
| 398 | 398 | ||
| 399 | -typedef FILE QEMUFile; | 399 | +typedef struct QEMUFile QEMUFile; |
| 400 | 400 | ||
| 401 | +QEMUFile *qemu_fopen(const char *filename, const char *mode); | ||
| 402 | +void qemu_fflush(QEMUFile *f); | ||
| 403 | +void qemu_fclose(QEMUFile *f); | ||
| 401 | void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size); | 404 | void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size); |
| 402 | void qemu_put_byte(QEMUFile *f, int v); | 405 | void qemu_put_byte(QEMUFile *f, int v); |
| 403 | void qemu_put_be16(QEMUFile *f, unsigned int v); | 406 | void qemu_put_be16(QEMUFile *f, unsigned int v); |
| @@ -467,8 +470,6 @@ int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence); | @@ -467,8 +470,6 @@ int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence); | ||
| 467 | typedef void SaveStateHandler(QEMUFile *f, void *opaque); | 470 | typedef void SaveStateHandler(QEMUFile *f, void *opaque); |
| 468 | typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id); | 471 | typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id); |
| 469 | 472 | ||
| 470 | -int qemu_loadvm(const char *filename); | ||
| 471 | -int qemu_savevm(const char *filename); | ||
| 472 | int register_savevm(const char *idstr, | 473 | int register_savevm(const char *idstr, |
| 473 | int instance_id, | 474 | int instance_id, |
| 474 | int version_id, | 475 | int version_id, |
| @@ -481,6 +482,11 @@ void qemu_put_timer(QEMUFile *f, QEMUTimer *ts); | @@ -481,6 +482,11 @@ void qemu_put_timer(QEMUFile *f, QEMUTimer *ts); | ||
| 481 | void cpu_save(QEMUFile *f, void *opaque); | 482 | void cpu_save(QEMUFile *f, void *opaque); |
| 482 | int cpu_load(QEMUFile *f, void *opaque, int version_id); | 483 | int cpu_load(QEMUFile *f, void *opaque, int version_id); |
| 483 | 484 | ||
| 485 | +void do_savevm(const char *name); | ||
| 486 | +void do_loadvm(const char *name); | ||
| 487 | +void do_delvm(const char *name); | ||
| 488 | +void do_info_snapshots(void); | ||
| 489 | + | ||
| 484 | /* bottom halves */ | 490 | /* bottom halves */ |
| 485 | typedef struct QEMUBH QEMUBH; | 491 | typedef struct QEMUBH QEMUBH; |
| 486 | typedef void QEMUBHFunc(void *opaque); | 492 | typedef void QEMUBHFunc(void *opaque); |
| @@ -504,6 +510,25 @@ extern BlockDriver bdrv_dmg; | @@ -504,6 +510,25 @@ extern BlockDriver bdrv_dmg; | ||
| 504 | extern BlockDriver bdrv_bochs; | 510 | extern BlockDriver bdrv_bochs; |
| 505 | extern BlockDriver bdrv_vpc; | 511 | extern BlockDriver bdrv_vpc; |
| 506 | extern BlockDriver bdrv_vvfat; | 512 | extern BlockDriver bdrv_vvfat; |
| 513 | +extern BlockDriver bdrv_qcow2; | ||
| 514 | + | ||
| 515 | +typedef struct BlockDriverInfo { | ||
| 516 | + /* in bytes, 0 if irrelevant */ | ||
| 517 | + int cluster_size; | ||
| 518 | + /* offset at which the VM state can be saved (0 if not possible) */ | ||
| 519 | + int64_t vm_state_offset; | ||
| 520 | +} BlockDriverInfo; | ||
| 521 | + | ||
| 522 | +typedef struct QEMUSnapshotInfo { | ||
| 523 | + char id_str[128]; /* unique snapshot id */ | ||
| 524 | + /* the following fields are informative. They are not needed for | ||
| 525 | + the consistency of the snapshot */ | ||
| 526 | + char name[256]; /* user choosen name */ | ||
| 527 | + uint32_t vm_state_size; /* VM state info size */ | ||
| 528 | + uint32_t date_sec; /* UTC date of the snapshot */ | ||
| 529 | + uint32_t date_nsec; | ||
| 530 | + uint64_t vm_clock_nsec; /* VM clock relative to boot */ | ||
| 531 | +} QEMUSnapshotInfo; | ||
| 507 | 532 | ||
| 508 | #define BDRV_O_RDONLY 0x0000 | 533 | #define BDRV_O_RDONLY 0x0000 |
| 509 | #define BDRV_O_RDWR 0x0002 | 534 | #define BDRV_O_RDWR 0x0002 |
| @@ -594,13 +619,22 @@ int bdrv_set_key(BlockDriverState *bs, const char *key); | @@ -594,13 +619,22 @@ int bdrv_set_key(BlockDriverState *bs, const char *key); | ||
| 594 | void bdrv_iterate_format(void (*it)(void *opaque, const char *name), | 619 | void bdrv_iterate_format(void (*it)(void *opaque, const char *name), |
| 595 | void *opaque); | 620 | void *opaque); |
| 596 | const char *bdrv_get_device_name(BlockDriverState *bs); | 621 | const char *bdrv_get_device_name(BlockDriverState *bs); |
| 622 | +int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num, | ||
| 623 | + const uint8_t *buf, int nb_sectors); | ||
| 624 | +int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi); | ||
| 597 | 625 | ||
| 598 | -int qcow_get_cluster_size(BlockDriverState *bs); | ||
| 599 | -int qcow_compress_cluster(BlockDriverState *bs, int64_t sector_num, | ||
| 600 | - const uint8_t *buf); | ||
| 601 | void bdrv_get_backing_filename(BlockDriverState *bs, | 626 | void bdrv_get_backing_filename(BlockDriverState *bs, |
| 602 | char *filename, int filename_size); | 627 | char *filename, int filename_size); |
| 603 | - | 628 | +int bdrv_snapshot_create(BlockDriverState *bs, |
| 629 | + QEMUSnapshotInfo *sn_info); | ||
| 630 | +int bdrv_snapshot_goto(BlockDriverState *bs, | ||
| 631 | + const char *snapshot_id); | ||
| 632 | +int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id); | ||
| 633 | +int bdrv_snapshot_list(BlockDriverState *bs, | ||
| 634 | + QEMUSnapshotInfo **psn_info); | ||
| 635 | +char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn); | ||
| 636 | + | ||
| 637 | +char *get_human_readable_size(char *buf, int buf_size, int64_t size); | ||
| 604 | int path_is_absolute(const char *path); | 638 | int path_is_absolute(const char *path); |
| 605 | void path_combine(char *dest, int dest_size, | 639 | void path_combine(char *dest, int dest_size, |
| 606 | const char *base_path, | 640 | const char *base_path, |
| @@ -824,7 +858,7 @@ void vnc_display_init(DisplayState *ds, int display); | @@ -824,7 +858,7 @@ void vnc_display_init(DisplayState *ds, int display); | ||
| 824 | /* ide.c */ | 858 | /* ide.c */ |
| 825 | #define MAX_DISKS 4 | 859 | #define MAX_DISKS 4 |
| 826 | 860 | ||
| 827 | -extern BlockDriverState *bs_table[MAX_DISKS]; | 861 | +extern BlockDriverState *bs_table[MAX_DISKS + 1]; |
| 828 | 862 | ||
| 829 | void isa_ide_init(int iobase, int iobase2, int irq, | 863 | void isa_ide_init(int iobase, int iobase2, int irq, |
| 830 | BlockDriverState *hd0, BlockDriverState *hd1); | 864 | BlockDriverState *hd0, BlockDriverState *hd1); |