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); | ... | ... |