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 | 494 | } |
| 495 | 495 | } |
| 496 | 496 | |
| 497 | -#if 0 | |
| 498 | 497 | /* not necessary now */ |
| 499 | 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 | 501 | uint8_t tmp_buf[SECTOR_SIZE]; |
| 504 | 502 | int len, nb_sectors, count; |
| 505 | 503 | int64_t sector_num; |
| ... | ... | @@ -542,9 +540,8 @@ static int bdrv_pread_em(BlockDriverState *bs, int64_t offset, |
| 542 | 540 | } |
| 543 | 541 | |
| 544 | 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 | 545 | uint8_t tmp_buf[SECTOR_SIZE]; |
| 549 | 546 | int len, nb_sectors, count; |
| 550 | 547 | int64_t sector_num; |
| ... | ... | @@ -589,7 +586,6 @@ static int bdrv_pwrite_em(BlockDriverState *bs, int64_t offset, |
| 589 | 586 | } |
| 590 | 587 | return count1; |
| 591 | 588 | } |
| 592 | -#endif | |
| 593 | 589 | |
| 594 | 590 | /** |
| 595 | 591 | * Read with byte offsets (needed only for file protocols) |
| ... | ... | @@ -602,7 +598,7 @@ int bdrv_pread(BlockDriverState *bs, int64_t offset, |
| 602 | 598 | if (!drv) |
| 603 | 599 | return -ENOENT; |
| 604 | 600 | if (!drv->bdrv_pread) |
| 605 | - return -ENOTSUP; | |
| 601 | + return bdrv_pread_em(bs, offset, buf1, count1); | |
| 606 | 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 | 613 | if (!drv) |
| 618 | 614 | return -ENOENT; |
| 619 | 615 | if (!drv->bdrv_pwrite) |
| 620 | - return -ENOTSUP; | |
| 616 | + return bdrv_pwrite_em(bs, offset, buf1, count1); | |
| 621 | 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 | 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 | 991 | /* async I/Os */ |
| ... | ... | @@ -1108,4 +1235,5 @@ void bdrv_init(void) |
| 1108 | 1235 | bdrv_register(&bdrv_bochs); |
| 1109 | 1236 | bdrv_register(&bdrv_vpc); |
| 1110 | 1237 | bdrv_register(&bdrv_vvfat); |
| 1238 | + bdrv_register(&bdrv_qcow2); | |
| 1111 | 1239 | } | ... | ... |
block_int.h
| ... | ... | @@ -57,6 +57,17 @@ struct BlockDriver { |
| 57 | 57 | const uint8_t *buf, int count); |
| 58 | 58 | int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset); |
| 59 | 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 | 72 | struct BlockDriver *next; |
| 62 | 73 | }; | ... | ... |
monitor.c
| ... | ... | @@ -380,18 +380,6 @@ static void do_log(const char *items) |
| 380 | 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 | 383 | static void do_stop(void) |
| 396 | 384 | { |
| 397 | 385 | vm_stop(EXCP_INTERRUPT); |
| ... | ... | @@ -1155,10 +1143,12 @@ static term_cmd_t term_cmds[] = { |
| 1155 | 1143 | "filename", "save screen into PPM image 'filename'" }, |
| 1156 | 1144 | { "log", "s", do_log, |
| 1157 | 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 | 1152 | { "stop", "", do_stop, |
| 1163 | 1153 | "", "stop emulation", }, |
| 1164 | 1154 | { "c|cont", "", do_cont, |
| ... | ... | @@ -1241,6 +1231,8 @@ static term_cmd_t info_cmds[] = { |
| 1241 | 1231 | "", "show profiling information", }, |
| 1242 | 1232 | { "capture", "", do_info_capture, |
| 1243 | 1233 | "show capture information" }, |
| 1234 | + { "snapshots", "", do_info_snapshots, | |
| 1235 | + "show the currently saved VM snapshots" }, | |
| 1244 | 1236 | { NULL, NULL, }, |
| 1245 | 1237 | }; |
| 1246 | 1238 | ... | ... |
qemu-img.c
| ... | ... | @@ -159,36 +159,6 @@ void help(void) |
| 159 | 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 | 162 | #if defined(WIN32) |
| 193 | 163 | /* XXX: put correct support for win32 */ |
| 194 | 164 | static int read_password(char *buf, int buf_size) |
| ... | ... | @@ -486,6 +456,7 @@ static int img_convert(int argc, char **argv) |
| 486 | 456 | int64_t total_sectors, nb_sectors, sector_num; |
| 487 | 457 | uint8_t buf[IO_BUF_SIZE]; |
| 488 | 458 | const uint8_t *buf1; |
| 459 | + BlockDriverInfo bdi; | |
| 489 | 460 | |
| 490 | 461 | fmt = NULL; |
| 491 | 462 | out_fmt = "raw"; |
| ... | ... | @@ -525,9 +496,9 @@ static int img_convert(int argc, char **argv) |
| 525 | 496 | drv = bdrv_find_format(out_fmt); |
| 526 | 497 | if (!drv) |
| 527 | 498 | error("Unknown file format '%s'", fmt); |
| 528 | - if (compress && drv != &bdrv_qcow) | |
| 499 | + if (compress && drv != &bdrv_qcow && drv != &bdrv_qcow2) | |
| 529 | 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 | 502 | error("Encryption not supported for this file format"); |
| 532 | 503 | if (compress && encrypt) |
| 533 | 504 | error("Compression and encryption not supported at the same time"); |
| ... | ... | @@ -544,7 +515,9 @@ static int img_convert(int argc, char **argv) |
| 544 | 515 | out_bs = bdrv_new_open(out_filename, out_fmt); |
| 545 | 516 | |
| 546 | 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 | 521 | if (cluster_size <= 0 || cluster_size > IO_BUF_SIZE) |
| 549 | 522 | error("invalid cluster size"); |
| 550 | 523 | cluster_sectors = cluster_size >> 9; |
| ... | ... | @@ -562,12 +535,15 @@ static int img_convert(int argc, char **argv) |
| 562 | 535 | if (n < cluster_sectors) |
| 563 | 536 | memset(buf + n * 512, 0, cluster_size - n * 512); |
| 564 | 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 | 540 | error("error while compressing sector %" PRId64, |
| 567 | 541 | sector_num); |
| 568 | 542 | } |
| 569 | 543 | sector_num += n; |
| 570 | 544 | } |
| 545 | + /* signal EOF to align */ | |
| 546 | + bdrv_write_compressed(out_bs, 0, NULL, 0); | |
| 571 | 547 | } else { |
| 572 | 548 | sector_num = 0; |
| 573 | 549 | for(;;) { |
| ... | ... | @@ -630,6 +606,24 @@ static int64_t get_allocated_file_size(const char *filename) |
| 630 | 606 | } |
| 631 | 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 | 627 | static int img_info(int argc, char **argv) |
| 634 | 628 | { |
| 635 | 629 | int c; |
| ... | ... | @@ -640,6 +634,7 @@ static int img_info(int argc, char **argv) |
| 640 | 634 | int64_t total_sectors, allocated_size; |
| 641 | 635 | char backing_filename[1024]; |
| 642 | 636 | char backing_filename2[1024]; |
| 637 | + BlockDriverInfo bdi; | |
| 643 | 638 | |
| 644 | 639 | fmt = NULL; |
| 645 | 640 | for(;;) { |
| ... | ... | @@ -690,13 +685,19 @@ static int img_info(int argc, char **argv) |
| 690 | 685 | dsize_buf); |
| 691 | 686 | if (bdrv_is_encrypted(bs)) |
| 692 | 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 | 692 | bdrv_get_backing_filename(bs, backing_filename, sizeof(backing_filename)); |
| 694 | - if (backing_filename[0] != '\0') | |
| 693 | + if (backing_filename[0] != '\0') { | |
| 695 | 694 | path_combine(backing_filename2, sizeof(backing_filename2), |
| 696 | 695 | filename, backing_filename); |
| 697 | 696 | printf("backing file: %s (actual path: %s)\n", |
| 698 | 697 | backing_filename, |
| 699 | 698 | backing_filename2); |
| 699 | + } | |
| 700 | + dump_snapshots(bs); | |
| 700 | 701 | bdrv_delete(bs); |
| 701 | 702 | return 0; |
| 702 | 703 | } | ... | ... |
vl.c
| ... | ... | @@ -113,7 +113,11 @@ char phys_ram_file[1024]; |
| 113 | 113 | void *ioport_opaque[MAX_IOPORTS]; |
| 114 | 114 | IOPortReadFunc *ioport_read_table[3][MAX_IOPORTS]; |
| 115 | 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 | 121 | int vga_ram_size; |
| 118 | 122 | int bios_size; |
| 119 | 123 | static DisplayState display_state; |
| ... | ... | @@ -4085,14 +4089,190 @@ void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque) |
| 4085 | 4089 | /***********************************************************/ |
| 4086 | 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 | 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 | 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 | 4278 | void qemu_put_be16(QEMUFile *f, unsigned int v) |
| ... | ... | @@ -4115,21 +4295,6 @@ void qemu_put_be64(QEMUFile *f, uint64_t v) |
| 4115 | 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 | 4298 | unsigned int qemu_get_be16(QEMUFile *f) |
| 4134 | 4299 | { |
| 4135 | 4300 | unsigned int v; |
| ... | ... | @@ -4156,18 +4321,6 @@ uint64_t qemu_get_be64(QEMUFile *f) |
| 4156 | 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 | 4324 | typedef struct SaveStateEntry { |
| 4172 | 4325 | char idstr[256]; |
| 4173 | 4326 | int instance_id; |
| ... | ... | @@ -4209,25 +4362,18 @@ int register_savevm(const char *idstr, |
| 4209 | 4362 | } |
| 4210 | 4363 | |
| 4211 | 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 | 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 | 4373 | qemu_put_be32(f, QEMU_VM_FILE_MAGIC); |
| 4230 | 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 | 4378 | for(se = first_se; se != NULL; se = se->next) { |
| 4233 | 4379 | /* ID string */ |
| ... | ... | @@ -4239,24 +4385,24 @@ int qemu_savevm(const char *filename) |
| 4239 | 4385 | qemu_put_be32(f, se->version_id); |
| 4240 | 4386 | |
| 4241 | 4387 | /* record size: filled later */ |
| 4242 | - len_pos = ftell(f); | |
| 4388 | + len_pos = qemu_ftell(f); | |
| 4243 | 4389 | qemu_put_be32(f, 0); |
| 4244 | 4390 | |
| 4245 | 4391 | se->save_state(f, se->opaque); |
| 4246 | 4392 | |
| 4247 | 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 | 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 | 4405 | ret = 0; |
| 4257 | - the_end: | |
| 4258 | - if (saved_vm_running) | |
| 4259 | - vm_start(); | |
| 4260 | 4406 | return ret; |
| 4261 | 4407 | } |
| 4262 | 4408 | |
| ... | ... | @@ -4272,38 +4418,29 @@ static SaveStateEntry *find_se(const char *idstr, int instance_id) |
| 4272 | 4418 | return NULL; |
| 4273 | 4419 | } |
| 4274 | 4420 | |
| 4275 | -int qemu_loadvm(const char *filename) | |
| 4421 | +int qemu_loadvm_state(QEMUFile *f) | |
| 4276 | 4422 | { |
| 4277 | 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 | 4426 | unsigned int v; |
| 4282 | 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 | 4429 | v = qemu_get_be32(f); |
| 4294 | 4430 | if (v != QEMU_VM_FILE_MAGIC) |
| 4295 | 4431 | goto fail; |
| 4296 | 4432 | v = qemu_get_be32(f); |
| 4297 | 4433 | if (v != QEMU_VM_FILE_VERSION) { |
| 4298 | 4434 | fail: |
| 4299 | - fclose(f); | |
| 4300 | 4435 | ret = -1; |
| 4301 | 4436 | goto the_end; |
| 4302 | 4437 | } |
| 4438 | + total_len = qemu_get_be64(f); | |
| 4439 | + end_pos = total_len + qemu_ftell(f); | |
| 4303 | 4440 | for(;;) { |
| 4304 | - len = qemu_get_byte(f); | |
| 4305 | - if (feof(f)) | |
| 4441 | + if (qemu_ftell(f) >= end_pos) | |
| 4306 | 4442 | break; |
| 4443 | + len = qemu_get_byte(f); | |
| 4307 | 4444 | qemu_get_buffer(f, idstr, len); |
| 4308 | 4445 | idstr[len] = '\0'; |
| 4309 | 4446 | instance_id = qemu_get_be32(f); |
| ... | ... | @@ -4313,7 +4450,7 @@ int qemu_loadvm(const char *filename) |
| 4313 | 4450 | printf("idstr=%s instance=0x%x version=%d len=%d\n", |
| 4314 | 4451 | idstr, instance_id, version_id, record_len); |
| 4315 | 4452 | #endif |
| 4316 | - cur_pos = ftell(f); | |
| 4453 | + cur_pos = qemu_ftell(f); | |
| 4317 | 4454 | se = find_se(idstr, instance_id); |
| 4318 | 4455 | if (!se) { |
| 4319 | 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 | 4465 | /* always seek to exact end of record */ |
| 4329 | 4466 | qemu_fseek(f, cur_pos + record_len, SEEK_SET); |
| 4330 | 4467 | } |
| 4331 | - fclose(f); | |
| 4332 | 4468 | ret = 0; |
| 4333 | 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 | 4611 | if (saved_vm_running) |
| 4335 | 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 | 6690 | } else |
| 6285 | 6691 | #endif |
| 6286 | 6692 | if (loadvm) |
| 6287 | - qemu_loadvm(loadvm); | |
| 6693 | + do_loadvm(loadvm); | |
| 6288 | 6694 | |
| 6289 | 6695 | { |
| 6290 | 6696 | /* XXX: simplify init */ | ... | ... |
vl.h
| ... | ... | @@ -396,8 +396,11 @@ void cpu_disable_ticks(void); |
| 396 | 396 | |
| 397 | 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 | 404 | void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size); |
| 402 | 405 | void qemu_put_byte(QEMUFile *f, int v); |
| 403 | 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 | 470 | typedef void SaveStateHandler(QEMUFile *f, void *opaque); |
| 468 | 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 | 473 | int register_savevm(const char *idstr, |
| 473 | 474 | int instance_id, |
| 474 | 475 | int version_id, |
| ... | ... | @@ -481,6 +482,11 @@ void qemu_put_timer(QEMUFile *f, QEMUTimer *ts); |
| 481 | 482 | void cpu_save(QEMUFile *f, void *opaque); |
| 482 | 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 | 490 | /* bottom halves */ |
| 485 | 491 | typedef struct QEMUBH QEMUBH; |
| 486 | 492 | typedef void QEMUBHFunc(void *opaque); |
| ... | ... | @@ -504,6 +510,25 @@ extern BlockDriver bdrv_dmg; |
| 504 | 510 | extern BlockDriver bdrv_bochs; |
| 505 | 511 | extern BlockDriver bdrv_vpc; |
| 506 | 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 | 533 | #define BDRV_O_RDONLY 0x0000 |
| 509 | 534 | #define BDRV_O_RDWR 0x0002 |
| ... | ... | @@ -594,13 +619,22 @@ int bdrv_set_key(BlockDriverState *bs, const char *key); |
| 594 | 619 | void bdrv_iterate_format(void (*it)(void *opaque, const char *name), |
| 595 | 620 | void *opaque); |
| 596 | 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 | 626 | void bdrv_get_backing_filename(BlockDriverState *bs, |
| 602 | 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 | 638 | int path_is_absolute(const char *path); |
| 605 | 639 | void path_combine(char *dest, int dest_size, |
| 606 | 640 | const char *base_path, |
| ... | ... | @@ -824,7 +858,7 @@ void vnc_display_init(DisplayState *ds, int display); |
| 824 | 858 | /* ide.c */ |
| 825 | 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 | 863 | void isa_ide_init(int iobase, int iobase2, int irq, |
| 830 | 864 | BlockDriverState *hd0, BlockDriverState *hd1); | ... | ... |