mirror of
https://github.com/qemu/qemu.git
synced 2024-11-23 19:03:38 +08:00
multiple snapshot support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2086 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
42ca638823
commit
faea38e786
144
block.c
144
block.c
@ -494,12 +494,10 @@ int bdrv_write(BlockDriverState *bs, int64_t sector_num,
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* not necessary now */
|
||||
static int bdrv_pread_em(BlockDriverState *bs, int64_t offset,
|
||||
void *buf1, int count1)
|
||||
uint8_t *buf, int count1)
|
||||
{
|
||||
uint8_t *buf = buf1;
|
||||
uint8_t tmp_buf[SECTOR_SIZE];
|
||||
int len, nb_sectors, count;
|
||||
int64_t sector_num;
|
||||
@ -542,9 +540,8 @@ static int bdrv_pread_em(BlockDriverState *bs, int64_t offset,
|
||||
}
|
||||
|
||||
static int bdrv_pwrite_em(BlockDriverState *bs, int64_t offset,
|
||||
const void *buf1, int count1)
|
||||
const uint8_t *buf, int count1)
|
||||
{
|
||||
const uint8_t *buf = buf1;
|
||||
uint8_t tmp_buf[SECTOR_SIZE];
|
||||
int len, nb_sectors, count;
|
||||
int64_t sector_num;
|
||||
@ -589,7 +586,6 @@ static int bdrv_pwrite_em(BlockDriverState *bs, int64_t offset,
|
||||
}
|
||||
return count1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Read with byte offsets (needed only for file protocols)
|
||||
@ -602,7 +598,7 @@ int bdrv_pread(BlockDriverState *bs, int64_t offset,
|
||||
if (!drv)
|
||||
return -ENOENT;
|
||||
if (!drv->bdrv_pread)
|
||||
return -ENOTSUP;
|
||||
return bdrv_pread_em(bs, offset, buf1, count1);
|
||||
return drv->bdrv_pread(bs, offset, buf1, count1);
|
||||
}
|
||||
|
||||
@ -617,7 +613,7 @@ int bdrv_pwrite(BlockDriverState *bs, int64_t offset,
|
||||
if (!drv)
|
||||
return -ENOENT;
|
||||
if (!drv->bdrv_pwrite)
|
||||
return -ENOTSUP;
|
||||
return bdrv_pwrite_em(bs, offset, buf1, count1);
|
||||
return drv->bdrv_pwrite(bs, offset, buf1, count1);
|
||||
}
|
||||
|
||||
@ -859,6 +855,137 @@ void bdrv_get_backing_filename(BlockDriverState *bs,
|
||||
}
|
||||
}
|
||||
|
||||
int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors)
|
||||
{
|
||||
BlockDriver *drv = bs->drv;
|
||||
if (!drv)
|
||||
return -ENOENT;
|
||||
if (!drv->bdrv_write_compressed)
|
||||
return -ENOTSUP;
|
||||
return drv->bdrv_write_compressed(bs, sector_num, buf, nb_sectors);
|
||||
}
|
||||
|
||||
int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
{
|
||||
BlockDriver *drv = bs->drv;
|
||||
if (!drv)
|
||||
return -ENOENT;
|
||||
if (!drv->bdrv_get_info)
|
||||
return -ENOTSUP;
|
||||
memset(bdi, 0, sizeof(*bdi));
|
||||
return drv->bdrv_get_info(bs, bdi);
|
||||
}
|
||||
|
||||
/**************************************************************/
|
||||
/* handling of snapshots */
|
||||
|
||||
int bdrv_snapshot_create(BlockDriverState *bs,
|
||||
QEMUSnapshotInfo *sn_info)
|
||||
{
|
||||
BlockDriver *drv = bs->drv;
|
||||
if (!drv)
|
||||
return -ENOENT;
|
||||
if (!drv->bdrv_snapshot_create)
|
||||
return -ENOTSUP;
|
||||
return drv->bdrv_snapshot_create(bs, sn_info);
|
||||
}
|
||||
|
||||
int bdrv_snapshot_goto(BlockDriverState *bs,
|
||||
const char *snapshot_id)
|
||||
{
|
||||
BlockDriver *drv = bs->drv;
|
||||
if (!drv)
|
||||
return -ENOENT;
|
||||
if (!drv->bdrv_snapshot_goto)
|
||||
return -ENOTSUP;
|
||||
return drv->bdrv_snapshot_goto(bs, snapshot_id);
|
||||
}
|
||||
|
||||
int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
|
||||
{
|
||||
BlockDriver *drv = bs->drv;
|
||||
if (!drv)
|
||||
return -ENOENT;
|
||||
if (!drv->bdrv_snapshot_delete)
|
||||
return -ENOTSUP;
|
||||
return drv->bdrv_snapshot_delete(bs, snapshot_id);
|
||||
}
|
||||
|
||||
int bdrv_snapshot_list(BlockDriverState *bs,
|
||||
QEMUSnapshotInfo **psn_info)
|
||||
{
|
||||
BlockDriver *drv = bs->drv;
|
||||
if (!drv)
|
||||
return -ENOENT;
|
||||
if (!drv->bdrv_snapshot_list)
|
||||
return -ENOTSUP;
|
||||
return drv->bdrv_snapshot_list(bs, psn_info);
|
||||
}
|
||||
|
||||
#define NB_SUFFIXES 4
|
||||
|
||||
char *get_human_readable_size(char *buf, int buf_size, int64_t size)
|
||||
{
|
||||
static const char suffixes[NB_SUFFIXES] = "KMGT";
|
||||
int64_t base;
|
||||
int i;
|
||||
|
||||
if (size <= 999) {
|
||||
snprintf(buf, buf_size, "%" PRId64, size);
|
||||
} else {
|
||||
base = 1024;
|
||||
for(i = 0; i < NB_SUFFIXES; i++) {
|
||||
if (size < (10 * base)) {
|
||||
snprintf(buf, buf_size, "%0.1f%c",
|
||||
(double)size / base,
|
||||
suffixes[i]);
|
||||
break;
|
||||
} else if (size < (1000 * base) || i == (NB_SUFFIXES - 1)) {
|
||||
snprintf(buf, buf_size, "%" PRId64 "%c",
|
||||
((size + (base >> 1)) / base),
|
||||
suffixes[i]);
|
||||
break;
|
||||
}
|
||||
base = base * 1024;
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn)
|
||||
{
|
||||
char buf1[128], date_buf[128], clock_buf[128];
|
||||
struct tm tm;
|
||||
time_t ti;
|
||||
int64_t secs;
|
||||
|
||||
if (!sn) {
|
||||
snprintf(buf, buf_size,
|
||||
"%-10s%-20s%7s%20s%15s",
|
||||
"ID", "TAG", "VM SIZE", "DATE", "VM CLOCK");
|
||||
} else {
|
||||
ti = sn->date_sec;
|
||||
localtime_r(&ti, &tm);
|
||||
strftime(date_buf, sizeof(date_buf),
|
||||
"%Y-%m-%d %H:%M:%S", &tm);
|
||||
secs = sn->vm_clock_nsec / 1000000000;
|
||||
snprintf(clock_buf, sizeof(clock_buf),
|
||||
"%02d:%02d:%02d.%03d",
|
||||
(int)(secs / 3600),
|
||||
(int)((secs / 60) % 60),
|
||||
(int)(secs % 60),
|
||||
(int)((sn->vm_clock_nsec / 1000000) % 1000));
|
||||
snprintf(buf, buf_size,
|
||||
"%-10s%-20s%7s%20s%15s",
|
||||
sn->id_str, sn->name,
|
||||
get_human_readable_size(buf1, sizeof(buf1), sn->vm_state_size),
|
||||
date_buf,
|
||||
clock_buf);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************/
|
||||
/* async I/Os */
|
||||
@ -1108,4 +1235,5 @@ void bdrv_init(void)
|
||||
bdrv_register(&bdrv_bochs);
|
||||
bdrv_register(&bdrv_vpc);
|
||||
bdrv_register(&bdrv_vvfat);
|
||||
bdrv_register(&bdrv_qcow2);
|
||||
}
|
||||
|
11
block_int.h
11
block_int.h
@ -57,6 +57,17 @@ struct BlockDriver {
|
||||
const uint8_t *buf, int count);
|
||||
int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset);
|
||||
int64_t (*bdrv_getlength)(BlockDriverState *bs);
|
||||
int (*bdrv_write_compressed)(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors);
|
||||
|
||||
int (*bdrv_snapshot_create)(BlockDriverState *bs,
|
||||
QEMUSnapshotInfo *sn_info);
|
||||
int (*bdrv_snapshot_goto)(BlockDriverState *bs,
|
||||
const char *snapshot_id);
|
||||
int (*bdrv_snapshot_delete)(BlockDriverState *bs, const char *snapshot_id);
|
||||
int (*bdrv_snapshot_list)(BlockDriverState *bs,
|
||||
QEMUSnapshotInfo **psn_info);
|
||||
int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
|
||||
|
||||
struct BlockDriver *next;
|
||||
};
|
||||
|
24
monitor.c
24
monitor.c
@ -380,18 +380,6 @@ static void do_log(const char *items)
|
||||
cpu_set_log(mask);
|
||||
}
|
||||
|
||||
static void do_savevm(const char *filename)
|
||||
{
|
||||
if (qemu_savevm(filename) < 0)
|
||||
term_printf("I/O error when saving VM to '%s'\n", filename);
|
||||
}
|
||||
|
||||
static void do_loadvm(const char *filename)
|
||||
{
|
||||
if (qemu_loadvm(filename) < 0)
|
||||
term_printf("I/O error when loading VM from '%s'\n", filename);
|
||||
}
|
||||
|
||||
static void do_stop(void)
|
||||
{
|
||||
vm_stop(EXCP_INTERRUPT);
|
||||
@ -1155,10 +1143,12 @@ static term_cmd_t term_cmds[] = {
|
||||
"filename", "save screen into PPM image 'filename'" },
|
||||
{ "log", "s", do_log,
|
||||
"item1[,...]", "activate logging of the specified items to '/tmp/qemu.log'" },
|
||||
{ "savevm", "F", do_savevm,
|
||||
"filename", "save the whole virtual machine state to 'filename'" },
|
||||
{ "loadvm", "F", do_loadvm,
|
||||
"filename", "restore the whole virtual machine state from 'filename'" },
|
||||
{ "savevm", "s?", do_savevm,
|
||||
"tag|id", "save a VM snapshot. If no tag or id are provided, a new snapshot is created" },
|
||||
{ "loadvm", "s", do_loadvm,
|
||||
"tag|id", "restore a VM snapshot from its tag or id" },
|
||||
{ "delvm", "s", do_delvm,
|
||||
"tag|id", "delete a VM snapshot from its tag or id" },
|
||||
{ "stop", "", do_stop,
|
||||
"", "stop emulation", },
|
||||
{ "c|cont", "", do_cont,
|
||||
@ -1241,6 +1231,8 @@ static term_cmd_t info_cmds[] = {
|
||||
"", "show profiling information", },
|
||||
{ "capture", "", do_info_capture,
|
||||
"show capture information" },
|
||||
{ "snapshots", "", do_info_snapshots,
|
||||
"show the currently saved VM snapshots" },
|
||||
{ NULL, NULL, },
|
||||
};
|
||||
|
||||
|
71
qemu-img.c
71
qemu-img.c
@ -159,36 +159,6 @@ void help(void)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
#define NB_SUFFIXES 4
|
||||
|
||||
static void get_human_readable_size(char *buf, int buf_size, int64_t size)
|
||||
{
|
||||
static const char suffixes[NB_SUFFIXES] = "KMGT";
|
||||
int64_t base;
|
||||
int i;
|
||||
|
||||
if (size <= 999) {
|
||||
snprintf(buf, buf_size, "%" PRId64, size);
|
||||
} else {
|
||||
base = 1024;
|
||||
for(i = 0; i < NB_SUFFIXES; i++) {
|
||||
if (size < (10 * base)) {
|
||||
snprintf(buf, buf_size, "%0.1f%c",
|
||||
(double)size / base,
|
||||
suffixes[i]);
|
||||
break;
|
||||
} else if (size < (1000 * base) || i == (NB_SUFFIXES - 1)) {
|
||||
snprintf(buf, buf_size, "%" PRId64 "%c",
|
||||
((size + (base >> 1)) / base),
|
||||
suffixes[i]);
|
||||
break;
|
||||
}
|
||||
base = base * 1024;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(WIN32)
|
||||
/* XXX: put correct support for win32 */
|
||||
static int read_password(char *buf, int buf_size)
|
||||
@ -486,6 +456,7 @@ static int img_convert(int argc, char **argv)
|
||||
int64_t total_sectors, nb_sectors, sector_num;
|
||||
uint8_t buf[IO_BUF_SIZE];
|
||||
const uint8_t *buf1;
|
||||
BlockDriverInfo bdi;
|
||||
|
||||
fmt = NULL;
|
||||
out_fmt = "raw";
|
||||
@ -525,9 +496,9 @@ static int img_convert(int argc, char **argv)
|
||||
drv = bdrv_find_format(out_fmt);
|
||||
if (!drv)
|
||||
error("Unknown file format '%s'", fmt);
|
||||
if (compress && drv != &bdrv_qcow)
|
||||
if (compress && drv != &bdrv_qcow && drv != &bdrv_qcow2)
|
||||
error("Compression not supported for this file format");
|
||||
if (encrypt && drv != &bdrv_qcow)
|
||||
if (encrypt && drv != &bdrv_qcow && drv != &bdrv_qcow2)
|
||||
error("Encryption not supported for this file format");
|
||||
if (compress && encrypt)
|
||||
error("Compression and encryption not supported at the same time");
|
||||
@ -544,7 +515,9 @@ static int img_convert(int argc, char **argv)
|
||||
out_bs = bdrv_new_open(out_filename, out_fmt);
|
||||
|
||||
if (compress) {
|
||||
cluster_size = qcow_get_cluster_size(out_bs);
|
||||
if (bdrv_get_info(out_bs, &bdi) < 0)
|
||||
error("could not get block driver info");
|
||||
cluster_size = bdi.cluster_size;
|
||||
if (cluster_size <= 0 || cluster_size > IO_BUF_SIZE)
|
||||
error("invalid cluster size");
|
||||
cluster_sectors = cluster_size >> 9;
|
||||
@ -562,12 +535,15 @@ static int img_convert(int argc, char **argv)
|
||||
if (n < cluster_sectors)
|
||||
memset(buf + n * 512, 0, cluster_size - n * 512);
|
||||
if (is_not_zero(buf, cluster_size)) {
|
||||
if (qcow_compress_cluster(out_bs, sector_num, buf) != 0)
|
||||
if (bdrv_write_compressed(out_bs, sector_num, buf,
|
||||
cluster_sectors) != 0)
|
||||
error("error while compressing sector %" PRId64,
|
||||
sector_num);
|
||||
}
|
||||
sector_num += n;
|
||||
}
|
||||
/* signal EOF to align */
|
||||
bdrv_write_compressed(out_bs, 0, NULL, 0);
|
||||
} else {
|
||||
sector_num = 0;
|
||||
for(;;) {
|
||||
@ -630,6 +606,24 @@ static int64_t get_allocated_file_size(const char *filename)
|
||||
}
|
||||
#endif
|
||||
|
||||
static void dump_snapshots(BlockDriverState *bs)
|
||||
{
|
||||
QEMUSnapshotInfo *sn_tab, *sn;
|
||||
int nb_sns, i;
|
||||
char buf[256];
|
||||
|
||||
nb_sns = bdrv_snapshot_list(bs, &sn_tab);
|
||||
if (nb_sns <= 0)
|
||||
return;
|
||||
printf("Snapshot list:\n");
|
||||
printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL));
|
||||
for(i = 0; i < nb_sns; i++) {
|
||||
sn = &sn_tab[i];
|
||||
printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn));
|
||||
}
|
||||
qemu_free(sn_tab);
|
||||
}
|
||||
|
||||
static int img_info(int argc, char **argv)
|
||||
{
|
||||
int c;
|
||||
@ -640,6 +634,7 @@ static int img_info(int argc, char **argv)
|
||||
int64_t total_sectors, allocated_size;
|
||||
char backing_filename[1024];
|
||||
char backing_filename2[1024];
|
||||
BlockDriverInfo bdi;
|
||||
|
||||
fmt = NULL;
|
||||
for(;;) {
|
||||
@ -690,13 +685,19 @@ static int img_info(int argc, char **argv)
|
||||
dsize_buf);
|
||||
if (bdrv_is_encrypted(bs))
|
||||
printf("encrypted: yes\n");
|
||||
if (bdrv_get_info(bs, &bdi) >= 0) {
|
||||
if (bdi.cluster_size != 0)
|
||||
printf("cluster_size: %d\n", bdi.cluster_size);
|
||||
}
|
||||
bdrv_get_backing_filename(bs, backing_filename, sizeof(backing_filename));
|
||||
if (backing_filename[0] != '\0')
|
||||
if (backing_filename[0] != '\0') {
|
||||
path_combine(backing_filename2, sizeof(backing_filename2),
|
||||
filename, backing_filename);
|
||||
printf("backing file: %s (actual path: %s)\n",
|
||||
backing_filename,
|
||||
backing_filename2);
|
||||
}
|
||||
dump_snapshots(bs);
|
||||
bdrv_delete(bs);
|
||||
return 0;
|
||||
}
|
||||
|
550
vl.c
550
vl.c
@ -113,7 +113,11 @@ char phys_ram_file[1024];
|
||||
void *ioport_opaque[MAX_IOPORTS];
|
||||
IOPortReadFunc *ioport_read_table[3][MAX_IOPORTS];
|
||||
IOPortWriteFunc *ioport_write_table[3][MAX_IOPORTS];
|
||||
BlockDriverState *bs_table[MAX_DISKS], *fd_table[MAX_FD];
|
||||
/* Note: bs_table[MAX_DISKS] is a dummy block driver if none available
|
||||
to store the VM snapshots */
|
||||
BlockDriverState *bs_table[MAX_DISKS + 1], *fd_table[MAX_FD];
|
||||
/* point to the block driver where the snapshots are managed */
|
||||
BlockDriverState *bs_snapshots;
|
||||
int vga_ram_size;
|
||||
int bios_size;
|
||||
static DisplayState display_state;
|
||||
@ -4085,14 +4089,190 @@ void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque)
|
||||
/***********************************************************/
|
||||
/* savevm/loadvm support */
|
||||
|
||||
#define IO_BUF_SIZE 32768
|
||||
|
||||
struct QEMUFile {
|
||||
FILE *outfile;
|
||||
BlockDriverState *bs;
|
||||
int is_file;
|
||||
int is_writable;
|
||||
int64_t base_offset;
|
||||
int64_t buf_offset; /* start of buffer when writing, end of buffer
|
||||
when reading */
|
||||
int buf_index;
|
||||
int buf_size; /* 0 when writing */
|
||||
uint8_t buf[IO_BUF_SIZE];
|
||||
};
|
||||
|
||||
QEMUFile *qemu_fopen(const char *filename, const char *mode)
|
||||
{
|
||||
QEMUFile *f;
|
||||
|
||||
f = qemu_mallocz(sizeof(QEMUFile));
|
||||
if (!f)
|
||||
return NULL;
|
||||
if (!strcmp(mode, "wb")) {
|
||||
f->is_writable = 1;
|
||||
} else if (!strcmp(mode, "rb")) {
|
||||
f->is_writable = 0;
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
f->outfile = fopen(filename, mode);
|
||||
if (!f->outfile)
|
||||
goto fail;
|
||||
f->is_file = 1;
|
||||
return f;
|
||||
fail:
|
||||
if (f->outfile)
|
||||
fclose(f->outfile);
|
||||
qemu_free(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int64_t offset, int is_writable)
|
||||
{
|
||||
QEMUFile *f;
|
||||
|
||||
f = qemu_mallocz(sizeof(QEMUFile));
|
||||
if (!f)
|
||||
return NULL;
|
||||
f->is_file = 0;
|
||||
f->bs = bs;
|
||||
f->is_writable = is_writable;
|
||||
f->base_offset = offset;
|
||||
return f;
|
||||
}
|
||||
|
||||
void qemu_fflush(QEMUFile *f)
|
||||
{
|
||||
if (!f->is_writable)
|
||||
return;
|
||||
if (f->buf_index > 0) {
|
||||
if (f->is_file) {
|
||||
fseek(f->outfile, f->buf_offset, SEEK_SET);
|
||||
fwrite(f->buf, 1, f->buf_index, f->outfile);
|
||||
} else {
|
||||
bdrv_pwrite(f->bs, f->base_offset + f->buf_offset,
|
||||
f->buf, f->buf_index);
|
||||
}
|
||||
f->buf_offset += f->buf_index;
|
||||
f->buf_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void qemu_fill_buffer(QEMUFile *f)
|
||||
{
|
||||
int len;
|
||||
|
||||
if (f->is_writable)
|
||||
return;
|
||||
if (f->is_file) {
|
||||
fseek(f->outfile, f->buf_offset, SEEK_SET);
|
||||
len = fread(f->buf, 1, IO_BUF_SIZE, f->outfile);
|
||||
if (len < 0)
|
||||
len = 0;
|
||||
} else {
|
||||
len = bdrv_pread(f->bs, f->base_offset + f->buf_offset,
|
||||
f->buf, IO_BUF_SIZE);
|
||||
if (len < 0)
|
||||
len = 0;
|
||||
}
|
||||
f->buf_index = 0;
|
||||
f->buf_size = len;
|
||||
f->buf_offset += len;
|
||||
}
|
||||
|
||||
void qemu_fclose(QEMUFile *f)
|
||||
{
|
||||
if (f->is_writable)
|
||||
qemu_fflush(f);
|
||||
if (f->is_file) {
|
||||
fclose(f->outfile);
|
||||
}
|
||||
qemu_free(f);
|
||||
}
|
||||
|
||||
void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
|
||||
{
|
||||
fwrite(buf, 1, size, f);
|
||||
int l;
|
||||
while (size > 0) {
|
||||
l = IO_BUF_SIZE - f->buf_index;
|
||||
if (l > size)
|
||||
l = size;
|
||||
memcpy(f->buf + f->buf_index, buf, l);
|
||||
f->buf_index += l;
|
||||
buf += l;
|
||||
size -= l;
|
||||
if (f->buf_index >= IO_BUF_SIZE)
|
||||
qemu_fflush(f);
|
||||
}
|
||||
}
|
||||
|
||||
void qemu_put_byte(QEMUFile *f, int v)
|
||||
{
|
||||
fputc(v, f);
|
||||
f->buf[f->buf_index++] = v;
|
||||
if (f->buf_index >= IO_BUF_SIZE)
|
||||
qemu_fflush(f);
|
||||
}
|
||||
|
||||
int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size1)
|
||||
{
|
||||
int size, l;
|
||||
|
||||
size = size1;
|
||||
while (size > 0) {
|
||||
l = f->buf_size - f->buf_index;
|
||||
if (l == 0) {
|
||||
qemu_fill_buffer(f);
|
||||
l = f->buf_size - f->buf_index;
|
||||
if (l == 0)
|
||||
break;
|
||||
}
|
||||
if (l > size)
|
||||
l = size;
|
||||
memcpy(buf, f->buf + f->buf_index, l);
|
||||
f->buf_index += l;
|
||||
buf += l;
|
||||
size -= l;
|
||||
}
|
||||
return size1 - size;
|
||||
}
|
||||
|
||||
int qemu_get_byte(QEMUFile *f)
|
||||
{
|
||||
if (f->buf_index >= f->buf_size) {
|
||||
qemu_fill_buffer(f);
|
||||
if (f->buf_index >= f->buf_size)
|
||||
return 0;
|
||||
}
|
||||
return f->buf[f->buf_index++];
|
||||
}
|
||||
|
||||
int64_t qemu_ftell(QEMUFile *f)
|
||||
{
|
||||
return f->buf_offset - f->buf_size + f->buf_index;
|
||||
}
|
||||
|
||||
int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence)
|
||||
{
|
||||
if (whence == SEEK_SET) {
|
||||
/* nothing to do */
|
||||
} else if (whence == SEEK_CUR) {
|
||||
pos += qemu_ftell(f);
|
||||
} else {
|
||||
/* SEEK_END not supported */
|
||||
return -1;
|
||||
}
|
||||
if (f->is_writable) {
|
||||
qemu_fflush(f);
|
||||
f->buf_offset = pos;
|
||||
} else {
|
||||
f->buf_offset = pos;
|
||||
f->buf_index = 0;
|
||||
f->buf_size = 0;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
void qemu_put_be16(QEMUFile *f, unsigned int v)
|
||||
@ -4115,21 +4295,6 @@ void qemu_put_be64(QEMUFile *f, uint64_t v)
|
||||
qemu_put_be32(f, v);
|
||||
}
|
||||
|
||||
int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size)
|
||||
{
|
||||
return fread(buf, 1, size, f);
|
||||
}
|
||||
|
||||
int qemu_get_byte(QEMUFile *f)
|
||||
{
|
||||
int v;
|
||||
v = fgetc(f);
|
||||
if (v == EOF)
|
||||
return 0;
|
||||
else
|
||||
return v;
|
||||
}
|
||||
|
||||
unsigned int qemu_get_be16(QEMUFile *f)
|
||||
{
|
||||
unsigned int v;
|
||||
@ -4156,18 +4321,6 @@ uint64_t qemu_get_be64(QEMUFile *f)
|
||||
return v;
|
||||
}
|
||||
|
||||
int64_t qemu_ftell(QEMUFile *f)
|
||||
{
|
||||
return ftell(f);
|
||||
}
|
||||
|
||||
int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence)
|
||||
{
|
||||
if (fseek(f, pos, whence) < 0)
|
||||
return -1;
|
||||
return ftell(f);
|
||||
}
|
||||
|
||||
typedef struct SaveStateEntry {
|
||||
char idstr[256];
|
||||
int instance_id;
|
||||
@ -4209,25 +4362,18 @@ int register_savevm(const char *idstr,
|
||||
}
|
||||
|
||||
#define QEMU_VM_FILE_MAGIC 0x5145564d
|
||||
#define QEMU_VM_FILE_VERSION 0x00000001
|
||||
#define QEMU_VM_FILE_VERSION 0x00000002
|
||||
|
||||
int qemu_savevm(const char *filename)
|
||||
int qemu_savevm_state(QEMUFile *f)
|
||||
{
|
||||
SaveStateEntry *se;
|
||||
QEMUFile *f;
|
||||
int len, len_pos, cur_pos, saved_vm_running, ret;
|
||||
|
||||
saved_vm_running = vm_running;
|
||||
vm_stop(0);
|
||||
|
||||
f = fopen(filename, "wb");
|
||||
if (!f) {
|
||||
ret = -1;
|
||||
goto the_end;
|
||||
}
|
||||
int len, ret;
|
||||
int64_t cur_pos, len_pos, total_len_pos;
|
||||
|
||||
qemu_put_be32(f, QEMU_VM_FILE_MAGIC);
|
||||
qemu_put_be32(f, QEMU_VM_FILE_VERSION);
|
||||
total_len_pos = qemu_ftell(f);
|
||||
qemu_put_be64(f, 0); /* total size */
|
||||
|
||||
for(se = first_se; se != NULL; se = se->next) {
|
||||
/* ID string */
|
||||
@ -4239,24 +4385,24 @@ int qemu_savevm(const char *filename)
|
||||
qemu_put_be32(f, se->version_id);
|
||||
|
||||
/* record size: filled later */
|
||||
len_pos = ftell(f);
|
||||
len_pos = qemu_ftell(f);
|
||||
qemu_put_be32(f, 0);
|
||||
|
||||
se->save_state(f, se->opaque);
|
||||
|
||||
/* fill record size */
|
||||
cur_pos = ftell(f);
|
||||
len = ftell(f) - len_pos - 4;
|
||||
fseek(f, len_pos, SEEK_SET);
|
||||
cur_pos = qemu_ftell(f);
|
||||
len = cur_pos - len_pos - 4;
|
||||
qemu_fseek(f, len_pos, SEEK_SET);
|
||||
qemu_put_be32(f, len);
|
||||
fseek(f, cur_pos, SEEK_SET);
|
||||
qemu_fseek(f, cur_pos, SEEK_SET);
|
||||
}
|
||||
cur_pos = qemu_ftell(f);
|
||||
qemu_fseek(f, total_len_pos, SEEK_SET);
|
||||
qemu_put_be64(f, cur_pos - total_len_pos - 8);
|
||||
qemu_fseek(f, cur_pos, SEEK_SET);
|
||||
|
||||
fclose(f);
|
||||
ret = 0;
|
||||
the_end:
|
||||
if (saved_vm_running)
|
||||
vm_start();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -4272,38 +4418,29 @@ static SaveStateEntry *find_se(const char *idstr, int instance_id)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int qemu_loadvm(const char *filename)
|
||||
int qemu_loadvm_state(QEMUFile *f)
|
||||
{
|
||||
SaveStateEntry *se;
|
||||
QEMUFile *f;
|
||||
int len, cur_pos, ret, instance_id, record_len, version_id;
|
||||
int saved_vm_running;
|
||||
int len, ret, instance_id, record_len, version_id;
|
||||
int64_t total_len, end_pos, cur_pos;
|
||||
unsigned int v;
|
||||
char idstr[256];
|
||||
|
||||
saved_vm_running = vm_running;
|
||||
vm_stop(0);
|
||||
|
||||
f = fopen(filename, "rb");
|
||||
if (!f) {
|
||||
ret = -1;
|
||||
goto the_end;
|
||||
}
|
||||
|
||||
v = qemu_get_be32(f);
|
||||
if (v != QEMU_VM_FILE_MAGIC)
|
||||
goto fail;
|
||||
v = qemu_get_be32(f);
|
||||
if (v != QEMU_VM_FILE_VERSION) {
|
||||
fail:
|
||||
fclose(f);
|
||||
ret = -1;
|
||||
goto the_end;
|
||||
}
|
||||
total_len = qemu_get_be64(f);
|
||||
end_pos = total_len + qemu_ftell(f);
|
||||
for(;;) {
|
||||
len = qemu_get_byte(f);
|
||||
if (feof(f))
|
||||
if (qemu_ftell(f) >= end_pos)
|
||||
break;
|
||||
len = qemu_get_byte(f);
|
||||
qemu_get_buffer(f, idstr, len);
|
||||
idstr[len] = '\0';
|
||||
instance_id = qemu_get_be32(f);
|
||||
@ -4313,7 +4450,7 @@ int qemu_loadvm(const char *filename)
|
||||
printf("idstr=%s instance=0x%x version=%d len=%d\n",
|
||||
idstr, instance_id, version_id, record_len);
|
||||
#endif
|
||||
cur_pos = ftell(f);
|
||||
cur_pos = qemu_ftell(f);
|
||||
se = find_se(idstr, instance_id);
|
||||
if (!se) {
|
||||
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)
|
||||
/* always seek to exact end of record */
|
||||
qemu_fseek(f, cur_pos + record_len, SEEK_SET);
|
||||
}
|
||||
fclose(f);
|
||||
ret = 0;
|
||||
the_end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* device can contain snapshots */
|
||||
static int bdrv_can_snapshot(BlockDriverState *bs)
|
||||
{
|
||||
return (bs &&
|
||||
!bdrv_is_removable(bs) &&
|
||||
!bdrv_is_read_only(bs));
|
||||
}
|
||||
|
||||
/* device must be snapshots in order to have a reliable snapshot */
|
||||
static int bdrv_has_snapshot(BlockDriverState *bs)
|
||||
{
|
||||
return (bs &&
|
||||
!bdrv_is_removable(bs) &&
|
||||
!bdrv_is_read_only(bs));
|
||||
}
|
||||
|
||||
static BlockDriverState *get_bs_snapshots(void)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
int i;
|
||||
|
||||
if (bs_snapshots)
|
||||
return bs_snapshots;
|
||||
for(i = 0; i <= MAX_DISKS; i++) {
|
||||
bs = bs_table[i];
|
||||
if (bdrv_can_snapshot(bs))
|
||||
goto ok;
|
||||
}
|
||||
return NULL;
|
||||
ok:
|
||||
bs_snapshots = bs;
|
||||
return bs;
|
||||
}
|
||||
|
||||
static int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info,
|
||||
const char *name)
|
||||
{
|
||||
QEMUSnapshotInfo *sn_tab, *sn;
|
||||
int nb_sns, i, ret;
|
||||
|
||||
ret = -ENOENT;
|
||||
nb_sns = bdrv_snapshot_list(bs, &sn_tab);
|
||||
if (nb_sns < 0)
|
||||
return ret;
|
||||
for(i = 0; i < nb_sns; i++) {
|
||||
sn = &sn_tab[i];
|
||||
if (!strcmp(sn->id_str, name) || !strcmp(sn->name, name)) {
|
||||
*sn_info = *sn;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
qemu_free(sn_tab);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void do_savevm(const char *name)
|
||||
{
|
||||
BlockDriverState *bs, *bs1;
|
||||
QEMUSnapshotInfo sn1, *sn = &sn1, old_sn1, *old_sn = &old_sn1;
|
||||
int must_delete, ret, i;
|
||||
BlockDriverInfo bdi1, *bdi = &bdi1;
|
||||
QEMUFile *f;
|
||||
int saved_vm_running;
|
||||
struct timeval tv;
|
||||
|
||||
bs = get_bs_snapshots();
|
||||
if (!bs) {
|
||||
term_printf("No block device can accept snapshots\n");
|
||||
return;
|
||||
}
|
||||
|
||||
saved_vm_running = vm_running;
|
||||
vm_stop(0);
|
||||
|
||||
must_delete = 0;
|
||||
if (name) {
|
||||
ret = bdrv_snapshot_find(bs, old_sn, name);
|
||||
if (ret >= 0) {
|
||||
must_delete = 1;
|
||||
}
|
||||
}
|
||||
memset(sn, 0, sizeof(*sn));
|
||||
if (must_delete) {
|
||||
pstrcpy(sn->name, sizeof(sn->name), old_sn->name);
|
||||
pstrcpy(sn->id_str, sizeof(sn->id_str), old_sn->id_str);
|
||||
} else {
|
||||
if (name)
|
||||
pstrcpy(sn->name, sizeof(sn->name), name);
|
||||
}
|
||||
|
||||
/* fill auxiliary fields */
|
||||
gettimeofday(&tv, NULL);
|
||||
sn->date_sec = tv.tv_sec;
|
||||
sn->date_nsec = tv.tv_usec * 1000;
|
||||
sn->vm_clock_nsec = qemu_get_clock(vm_clock);
|
||||
|
||||
if (bdrv_get_info(bs, bdi) < 0 || bdi->vm_state_offset <= 0) {
|
||||
term_printf("Device %s does not support VM state snapshots\n",
|
||||
bdrv_get_device_name(bs));
|
||||
goto the_end;
|
||||
}
|
||||
|
||||
/* save the VM state */
|
||||
f = qemu_fopen_bdrv(bs, bdi->vm_state_offset, 1);
|
||||
if (!f) {
|
||||
term_printf("Could not open VM state file\n");
|
||||
goto the_end;
|
||||
}
|
||||
ret = qemu_savevm_state(f);
|
||||
sn->vm_state_size = qemu_ftell(f);
|
||||
qemu_fclose(f);
|
||||
if (ret < 0) {
|
||||
term_printf("Error %d while writing VM\n", ret);
|
||||
goto the_end;
|
||||
}
|
||||
|
||||
/* create the snapshots */
|
||||
|
||||
for(i = 0; i < MAX_DISKS; i++) {
|
||||
bs1 = bs_table[i];
|
||||
if (bdrv_has_snapshot(bs1)) {
|
||||
if (must_delete) {
|
||||
ret = bdrv_snapshot_delete(bs1, old_sn->id_str);
|
||||
if (ret < 0) {
|
||||
term_printf("Error while deleting snapshot on '%s'\n",
|
||||
bdrv_get_device_name(bs1));
|
||||
}
|
||||
}
|
||||
ret = bdrv_snapshot_create(bs1, sn);
|
||||
if (ret < 0) {
|
||||
term_printf("Error while creating snapshot on '%s'\n",
|
||||
bdrv_get_device_name(bs1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
the_end:
|
||||
if (saved_vm_running)
|
||||
vm_start();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void do_loadvm(const char *name)
|
||||
{
|
||||
BlockDriverState *bs, *bs1;
|
||||
BlockDriverInfo bdi1, *bdi = &bdi1;
|
||||
QEMUFile *f;
|
||||
int i, ret;
|
||||
int saved_vm_running;
|
||||
|
||||
bs = get_bs_snapshots();
|
||||
if (!bs) {
|
||||
term_printf("No block device supports snapshots\n");
|
||||
return;
|
||||
}
|
||||
|
||||
saved_vm_running = vm_running;
|
||||
vm_stop(0);
|
||||
|
||||
for(i = 0; i <= MAX_DISKS; i++) {
|
||||
bs1 = bs_table[i];
|
||||
if (bdrv_has_snapshot(bs1)) {
|
||||
ret = bdrv_snapshot_goto(bs1, name);
|
||||
if (ret < 0) {
|
||||
if (bs != bs1)
|
||||
term_printf("Warning: ");
|
||||
switch(ret) {
|
||||
case -ENOTSUP:
|
||||
term_printf("Snapshots not supported on device '%s'\n",
|
||||
bdrv_get_device_name(bs1));
|
||||
break;
|
||||
case -ENOENT:
|
||||
term_printf("Could not find snapshot '%s' on device '%s'\n",
|
||||
name, bdrv_get_device_name(bs1));
|
||||
break;
|
||||
default:
|
||||
term_printf("Error %d while activating snapshot on '%s'\n",
|
||||
ret, bdrv_get_device_name(bs1));
|
||||
break;
|
||||
}
|
||||
/* fatal on snapshot block device */
|
||||
if (bs == bs1)
|
||||
goto the_end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bdrv_get_info(bs, bdi) < 0 || bdi->vm_state_offset <= 0) {
|
||||
term_printf("Device %s does not support VM state snapshots\n",
|
||||
bdrv_get_device_name(bs));
|
||||
return;
|
||||
}
|
||||
|
||||
/* restore the VM state */
|
||||
f = qemu_fopen_bdrv(bs, bdi->vm_state_offset, 0);
|
||||
if (!f) {
|
||||
term_printf("Could not open VM state file\n");
|
||||
goto the_end;
|
||||
}
|
||||
ret = qemu_loadvm_state(f);
|
||||
qemu_fclose(f);
|
||||
if (ret < 0) {
|
||||
term_printf("Error %d while loading VM state\n", ret);
|
||||
}
|
||||
the_end:
|
||||
if (saved_vm_running)
|
||||
vm_start();
|
||||
}
|
||||
|
||||
void do_delvm(const char *name)
|
||||
{
|
||||
BlockDriverState *bs, *bs1;
|
||||
int i, ret;
|
||||
|
||||
bs = get_bs_snapshots();
|
||||
if (!bs) {
|
||||
term_printf("No block device supports snapshots\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for(i = 0; i <= MAX_DISKS; i++) {
|
||||
bs1 = bs_table[i];
|
||||
if (bdrv_has_snapshot(bs1)) {
|
||||
ret = bdrv_snapshot_delete(bs1, name);
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOTSUP)
|
||||
term_printf("Snapshots not supported on device '%s'\n",
|
||||
bdrv_get_device_name(bs1));
|
||||
else
|
||||
term_printf("Error %d while deleting snapshot on '%s'\n",
|
||||
ret, bdrv_get_device_name(bs1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void do_info_snapshots(void)
|
||||
{
|
||||
BlockDriverState *bs, *bs1;
|
||||
QEMUSnapshotInfo *sn_tab, *sn;
|
||||
int nb_sns, i;
|
||||
char buf[256];
|
||||
|
||||
bs = get_bs_snapshots();
|
||||
if (!bs) {
|
||||
term_printf("No available block device supports snapshots\n");
|
||||
return;
|
||||
}
|
||||
term_printf("Snapshot devices:");
|
||||
for(i = 0; i <= MAX_DISKS; i++) {
|
||||
bs1 = bs_table[i];
|
||||
if (bdrv_has_snapshot(bs1)) {
|
||||
if (bs == bs1)
|
||||
term_printf(" %s", bdrv_get_device_name(bs1));
|
||||
}
|
||||
}
|
||||
term_printf("\n");
|
||||
|
||||
nb_sns = bdrv_snapshot_list(bs, &sn_tab);
|
||||
if (nb_sns < 0) {
|
||||
term_printf("bdrv_snapshot_list: error %d\n", nb_sns);
|
||||
return;
|
||||
}
|
||||
term_printf("Snapshot list (from %s):\n", bdrv_get_device_name(bs));
|
||||
term_printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL));
|
||||
for(i = 0; i < nb_sns; i++) {
|
||||
sn = &sn_tab[i];
|
||||
term_printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn));
|
||||
}
|
||||
qemu_free(sn_tab);
|
||||
}
|
||||
|
||||
/***********************************************************/
|
||||
@ -6284,7 +6690,7 @@ int main(int argc, char **argv)
|
||||
} else
|
||||
#endif
|
||||
if (loadvm)
|
||||
qemu_loadvm(loadvm);
|
||||
do_loadvm(loadvm);
|
||||
|
||||
{
|
||||
/* XXX: simplify init */
|
||||
|
48
vl.h
48
vl.h
@ -396,8 +396,11 @@ void cpu_disable_ticks(void);
|
||||
|
||||
/* VM Load/Save */
|
||||
|
||||
typedef FILE QEMUFile;
|
||||
typedef struct QEMUFile QEMUFile;
|
||||
|
||||
QEMUFile *qemu_fopen(const char *filename, const char *mode);
|
||||
void qemu_fflush(QEMUFile *f);
|
||||
void qemu_fclose(QEMUFile *f);
|
||||
void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size);
|
||||
void qemu_put_byte(QEMUFile *f, int v);
|
||||
void qemu_put_be16(QEMUFile *f, unsigned int v);
|
||||
@ -467,8 +470,6 @@ int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence);
|
||||
typedef void SaveStateHandler(QEMUFile *f, void *opaque);
|
||||
typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id);
|
||||
|
||||
int qemu_loadvm(const char *filename);
|
||||
int qemu_savevm(const char *filename);
|
||||
int register_savevm(const char *idstr,
|
||||
int instance_id,
|
||||
int version_id,
|
||||
@ -481,6 +482,11 @@ void qemu_put_timer(QEMUFile *f, QEMUTimer *ts);
|
||||
void cpu_save(QEMUFile *f, void *opaque);
|
||||
int cpu_load(QEMUFile *f, void *opaque, int version_id);
|
||||
|
||||
void do_savevm(const char *name);
|
||||
void do_loadvm(const char *name);
|
||||
void do_delvm(const char *name);
|
||||
void do_info_snapshots(void);
|
||||
|
||||
/* bottom halves */
|
||||
typedef struct QEMUBH QEMUBH;
|
||||
typedef void QEMUBHFunc(void *opaque);
|
||||
@ -504,6 +510,25 @@ extern BlockDriver bdrv_dmg;
|
||||
extern BlockDriver bdrv_bochs;
|
||||
extern BlockDriver bdrv_vpc;
|
||||
extern BlockDriver bdrv_vvfat;
|
||||
extern BlockDriver bdrv_qcow2;
|
||||
|
||||
typedef struct BlockDriverInfo {
|
||||
/* in bytes, 0 if irrelevant */
|
||||
int cluster_size;
|
||||
/* offset at which the VM state can be saved (0 if not possible) */
|
||||
int64_t vm_state_offset;
|
||||
} BlockDriverInfo;
|
||||
|
||||
typedef struct QEMUSnapshotInfo {
|
||||
char id_str[128]; /* unique snapshot id */
|
||||
/* the following fields are informative. They are not needed for
|
||||
the consistency of the snapshot */
|
||||
char name[256]; /* user choosen name */
|
||||
uint32_t vm_state_size; /* VM state info size */
|
||||
uint32_t date_sec; /* UTC date of the snapshot */
|
||||
uint32_t date_nsec;
|
||||
uint64_t vm_clock_nsec; /* VM clock relative to boot */
|
||||
} QEMUSnapshotInfo;
|
||||
|
||||
#define BDRV_O_RDONLY 0x0000
|
||||
#define BDRV_O_RDWR 0x0002
|
||||
@ -594,13 +619,22 @@ int bdrv_set_key(BlockDriverState *bs, const char *key);
|
||||
void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
|
||||
void *opaque);
|
||||
const char *bdrv_get_device_name(BlockDriverState *bs);
|
||||
int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf, int nb_sectors);
|
||||
int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi);
|
||||
|
||||
int qcow_get_cluster_size(BlockDriverState *bs);
|
||||
int qcow_compress_cluster(BlockDriverState *bs, int64_t sector_num,
|
||||
const uint8_t *buf);
|
||||
void bdrv_get_backing_filename(BlockDriverState *bs,
|
||||
char *filename, int filename_size);
|
||||
int bdrv_snapshot_create(BlockDriverState *bs,
|
||||
QEMUSnapshotInfo *sn_info);
|
||||
int bdrv_snapshot_goto(BlockDriverState *bs,
|
||||
const char *snapshot_id);
|
||||
int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id);
|
||||
int bdrv_snapshot_list(BlockDriverState *bs,
|
||||
QEMUSnapshotInfo **psn_info);
|
||||
char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn);
|
||||
|
||||
char *get_human_readable_size(char *buf, int buf_size, int64_t size);
|
||||
int path_is_absolute(const char *path);
|
||||
void path_combine(char *dest, int dest_size,
|
||||
const char *base_path,
|
||||
@ -824,7 +858,7 @@ void vnc_display_init(DisplayState *ds, int display);
|
||||
/* ide.c */
|
||||
#define MAX_DISKS 4
|
||||
|
||||
extern BlockDriverState *bs_table[MAX_DISKS];
|
||||
extern BlockDriverState *bs_table[MAX_DISKS + 1];
|
||||
|
||||
void isa_ide_init(int iobase, int iobase2, int irq,
|
||||
BlockDriverState *hd0, BlockDriverState *hd1);
|
||||
|
Loading…
Reference in New Issue
Block a user