Merge remote branch 'kwolf/for-anthony' into staging
This commit is contained in:
commit
aab2e8f79a
11
block.c
11
block.c
@ -745,6 +745,7 @@ int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res)
|
||||
int bdrv_commit(BlockDriverState *bs)
|
||||
{
|
||||
BlockDriver *drv = bs->drv;
|
||||
BlockDriver *backing_drv;
|
||||
int64_t sector, total_sectors;
|
||||
int n, ro, open_flags;
|
||||
int ret = 0, rw_ret = 0;
|
||||
@ -763,6 +764,7 @@ int bdrv_commit(BlockDriverState *bs)
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
backing_drv = bs->backing_hd->drv;
|
||||
ro = bs->backing_hd->read_only;
|
||||
strncpy(filename, bs->backing_hd->filename, sizeof(filename));
|
||||
open_flags = bs->backing_hd->open_flags;
|
||||
@ -772,12 +774,14 @@ int bdrv_commit(BlockDriverState *bs)
|
||||
bdrv_delete(bs->backing_hd);
|
||||
bs->backing_hd = NULL;
|
||||
bs_rw = bdrv_new("");
|
||||
rw_ret = bdrv_open(bs_rw, filename, open_flags | BDRV_O_RDWR, drv);
|
||||
rw_ret = bdrv_open(bs_rw, filename, open_flags | BDRV_O_RDWR,
|
||||
backing_drv);
|
||||
if (rw_ret < 0) {
|
||||
bdrv_delete(bs_rw);
|
||||
/* try to re-open read-only */
|
||||
bs_ro = bdrv_new("");
|
||||
ret = bdrv_open(bs_ro, filename, open_flags & ~BDRV_O_RDWR, drv);
|
||||
ret = bdrv_open(bs_ro, filename, open_flags & ~BDRV_O_RDWR,
|
||||
backing_drv);
|
||||
if (ret < 0) {
|
||||
bdrv_delete(bs_ro);
|
||||
/* drive not functional anymore */
|
||||
@ -828,7 +832,8 @@ ro_cleanup:
|
||||
bdrv_delete(bs->backing_hd);
|
||||
bs->backing_hd = NULL;
|
||||
bs_ro = bdrv_new("");
|
||||
ret = bdrv_open(bs_ro, filename, open_flags & ~BDRV_O_RDWR, drv);
|
||||
ret = bdrv_open(bs_ro, filename, open_flags & ~BDRV_O_RDWR,
|
||||
backing_drv);
|
||||
if (ret < 0) {
|
||||
bdrv_delete(bs_ro);
|
||||
/* drive not functional anymore */
|
||||
|
2
block.h
2
block.h
@ -35,7 +35,7 @@ typedef struct QEMUSnapshotInfo {
|
||||
#define BDRV_O_NO_BACKING 0x0100 /* don't open the backing file */
|
||||
#define BDRV_O_NO_FLUSH 0x0200 /* disable flushing on this disk */
|
||||
|
||||
#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB)
|
||||
#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH)
|
||||
|
||||
#define BDRV_SECTOR_BITS 9
|
||||
#define BDRV_SECTOR_SIZE (1ULL << BDRV_SECTOR_BITS)
|
||||
|
62
block/nbd.c
62
block/nbd.c
@ -33,6 +33,8 @@
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define EN_OPTSTR ":exportname="
|
||||
|
||||
typedef struct BDRVNBDState {
|
||||
int sock;
|
||||
off_t size;
|
||||
@ -42,55 +44,83 @@ typedef struct BDRVNBDState {
|
||||
static int nbd_open(BlockDriverState *bs, const char* filename, int flags)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
uint32_t nbdflags;
|
||||
|
||||
char *file;
|
||||
char *name;
|
||||
const char *host;
|
||||
const char *unixpath;
|
||||
int sock;
|
||||
off_t size;
|
||||
size_t blocksize;
|
||||
int ret;
|
||||
int err = -EINVAL;
|
||||
|
||||
if (!strstart(filename, "nbd:", &host))
|
||||
return -EINVAL;
|
||||
file = qemu_strdup(filename);
|
||||
|
||||
name = strstr(file, EN_OPTSTR);
|
||||
if (name) {
|
||||
if (name[strlen(EN_OPTSTR)] == 0) {
|
||||
goto out;
|
||||
}
|
||||
name[0] = 0;
|
||||
name += strlen(EN_OPTSTR);
|
||||
}
|
||||
|
||||
if (!strstart(file, "nbd:", &host)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (strstart(host, "unix:", &unixpath)) {
|
||||
|
||||
if (unixpath[0] != '/')
|
||||
return -EINVAL;
|
||||
if (unixpath[0] != '/') {
|
||||
goto out;
|
||||
}
|
||||
|
||||
sock = unix_socket_outgoing(unixpath);
|
||||
|
||||
} else {
|
||||
uint16_t port;
|
||||
uint16_t port = NBD_DEFAULT_PORT;
|
||||
char *p, *r;
|
||||
char hostname[128];
|
||||
|
||||
pstrcpy(hostname, 128, host);
|
||||
|
||||
p = strchr(hostname, ':');
|
||||
if (p == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (p != NULL) {
|
||||
*p = '\0';
|
||||
p++;
|
||||
|
||||
port = strtol(p, &r, 0);
|
||||
if (r == p)
|
||||
return -EINVAL;
|
||||
if (r == p) {
|
||||
goto out;
|
||||
}
|
||||
} else if (name == NULL) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
sock = tcp_socket_outgoing(hostname, port);
|
||||
}
|
||||
|
||||
if (sock == -1)
|
||||
return -errno;
|
||||
if (sock == -1) {
|
||||
err = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = nbd_receive_negotiate(sock, &size, &blocksize);
|
||||
if (ret == -1)
|
||||
return -errno;
|
||||
ret = nbd_receive_negotiate(sock, name, &nbdflags, &size, &blocksize);
|
||||
if (ret == -1) {
|
||||
err = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
s->sock = sock;
|
||||
s->size = size;
|
||||
s->blocksize = blocksize;
|
||||
err = 0;
|
||||
|
||||
return 0;
|
||||
out:
|
||||
qemu_free(file);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int nbd_read(BlockDriverState *bs, int64_t sector_num,
|
||||
|
@ -655,7 +655,7 @@ static int write_l2_entries(BlockDriverState *bs, uint64_t *l2_table,
|
||||
int ret;
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE);
|
||||
ret = bdrv_pwrite_sync(bs->file, l2_offset + start_offset,
|
||||
ret = bdrv_pwrite(bs->file, l2_offset + start_offset,
|
||||
&l2_table[l2_start_index], len);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
@ -718,9 +718,17 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
|
||||
goto err;
|
||||
}
|
||||
|
||||
for (i = 0; i < j; i++)
|
||||
/*
|
||||
* If this was a COW, we need to decrease the refcount of the old cluster.
|
||||
* Also flush bs->file to get the right order for L2 and refcount update.
|
||||
*/
|
||||
if (j != 0) {
|
||||
bdrv_flush(bs->file);
|
||||
for (i = 0; i < j; i++) {
|
||||
qcow2_free_any_clusters(bs,
|
||||
be64_to_cpu(old_cluster[i]) & ~QCOW_OFLAG_COPIED, 1);
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
err:
|
||||
|
@ -48,6 +48,7 @@
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/param.h>
|
||||
#include <linux/cdrom.h>
|
||||
#include <linux/fd.h>
|
||||
#endif
|
||||
@ -868,9 +869,14 @@ static int hdev_open(BlockDriverState *bs, const char *filename, int flags)
|
||||
|
||||
s->type = FTYPE_FILE;
|
||||
#if defined(__linux__)
|
||||
if (strstart(filename, "/dev/sg", NULL)) {
|
||||
{
|
||||
char resolved_path[ MAXPATHLEN ], *temp;
|
||||
|
||||
temp = realpath(filename, resolved_path);
|
||||
if (temp && strstart(temp, "/dev/sg", NULL)) {
|
||||
bs->sg = 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return raw_open_common(bs, filename, flags, 0);
|
||||
@ -1154,9 +1160,6 @@ static int cdrom_probe_device(const char *filename)
|
||||
int fd, ret;
|
||||
int prio = 0;
|
||||
|
||||
if (strstart(filename, "/dev/cd", NULL))
|
||||
prio = 50;
|
||||
|
||||
fd = open(filename, O_RDONLY | O_NONBLOCK);
|
||||
if (fd < 0) {
|
||||
goto out;
|
||||
|
@ -8,16 +8,6 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <netdb.h>
|
||||
#include <netinet/tcp.h>
|
||||
|
||||
#define closesocket(s) close(s)
|
||||
#endif
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "qemu-error.h"
|
||||
|
@ -512,7 +512,7 @@ static inline uint8_t fat_chksum(const direntry_t* entry)
|
||||
for(i=0;i<11;i++) {
|
||||
unsigned char c;
|
||||
|
||||
c = (i <= 8) ? entry->name[i] : entry->extension[i-8];
|
||||
c = (i < 8) ? entry->name[i] : entry->extension[i-8];
|
||||
chksum=(((chksum&0xfe)>>1)|((chksum&0x01)?0x80:0)) + c;
|
||||
}
|
||||
|
||||
|
@ -139,6 +139,7 @@ static void ide_identify(IDEState *s)
|
||||
put_le16(p + 61, s->nb_sectors >> 16);
|
||||
put_le16(p + 62, 0x07); /* single word dma0-2 supported */
|
||||
put_le16(p + 63, 0x07); /* mdma0-2 supported */
|
||||
put_le16(p + 64, 0x03); /* pio3-4 supported */
|
||||
put_le16(p + 65, 120);
|
||||
put_le16(p + 66, 120);
|
||||
put_le16(p + 67, 120);
|
||||
@ -199,13 +200,12 @@ static void ide_atapi_identify(IDEState *s)
|
||||
put_le16(p + 53, 7); /* words 64-70, 54-58, 88 valid */
|
||||
put_le16(p + 62, 7); /* single word dma0-2 supported */
|
||||
put_le16(p + 63, 7); /* mdma0-2 supported */
|
||||
put_le16(p + 64, 0x3f); /* PIO modes supported */
|
||||
#else
|
||||
put_le16(p + 49, 1 << 9); /* LBA supported, no DMA */
|
||||
put_le16(p + 53, 3); /* words 64-70, 54-58 valid */
|
||||
put_le16(p + 63, 0x103); /* DMA modes XXX: may be incorrect */
|
||||
put_le16(p + 64, 1); /* PIO modes */
|
||||
#endif
|
||||
put_le16(p + 64, 3); /* pio3-4 supported */
|
||||
put_le16(p + 65, 0xb4); /* minimum DMA multiword tx cycle time */
|
||||
put_le16(p + 66, 0xb4); /* recommended DMA multiword tx cycle time */
|
||||
put_le16(p + 67, 0x12c); /* minimum PIO cycle time without flow control */
|
||||
|
@ -600,7 +600,7 @@ static void lsi_queue_command(LSIState *s)
|
||||
{
|
||||
lsi_request *p = s->current;
|
||||
|
||||
DPRINTF("Queueing tag=0x%x\n", s->current_tag);
|
||||
DPRINTF("Queueing tag=0x%x\n", p->tag);
|
||||
assert(s->current != NULL);
|
||||
assert(s->current->dma_len == 0);
|
||||
QTAILQ_INSERT_TAIL(&s->queue, s->current, next);
|
||||
@ -880,7 +880,7 @@ static void lsi_do_msgout(LSIState *s)
|
||||
break;
|
||||
case 0x20: /* SIMPLE queue */
|
||||
s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID;
|
||||
DPRINTF("SIMPLE queue tag=0x%x\n", s->current_tag & 0xff);
|
||||
DPRINTF("SIMPLE queue tag=0x%x\n", s->select_tag & 0xff);
|
||||
break;
|
||||
case 0x21: /* HEAD of queue */
|
||||
BADF("HEAD queue not implemented\n");
|
||||
|
126
hw/scsi-disk.c
126
hw/scsi-disk.c
@ -135,7 +135,7 @@ static void scsi_read_complete(void * opaque, int ret)
|
||||
scsi_command_complete(r, CHECK_CONDITION, NO_SENSE);
|
||||
return;
|
||||
}
|
||||
DPRINTF("Data ready tag=0x%x len=%" PRId64 "\n", r->req.tag, r->iov.iov_len);
|
||||
DPRINTF("Data ready tag=0x%x len=%zd\n", r->req.tag, r->iov.iov_len);
|
||||
|
||||
r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->iov.iov_len);
|
||||
}
|
||||
@ -155,7 +155,7 @@ static void scsi_read_data(SCSIDevice *d, uint32_t tag)
|
||||
return;
|
||||
}
|
||||
if (r->sector_count == (uint32_t)-1) {
|
||||
DPRINTF("Read buf_len=%" PRId64 "\n", r->iov.iov_len);
|
||||
DPRINTF("Read buf_len=%zd\n", r->iov.iov_len);
|
||||
r->sector_count = 0;
|
||||
r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->iov.iov_len);
|
||||
return;
|
||||
@ -486,16 +486,26 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
|
||||
return buflen;
|
||||
}
|
||||
|
||||
static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p)
|
||||
static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p,
|
||||
int page_control)
|
||||
{
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
|
||||
BlockDriverState *bdrv = s->bs;
|
||||
int cylinders, heads, secs;
|
||||
|
||||
/*
|
||||
* If Changeable Values are requested, a mask denoting those mode parameters
|
||||
* that are changeable shall be returned. As we currently don't support
|
||||
* parameter changes via MODE_SELECT all bits are returned set to zero.
|
||||
* The buffer was already menset to zero by the caller of this function.
|
||||
*/
|
||||
switch (page) {
|
||||
case 4: /* Rigid disk device geometry page. */
|
||||
p[0] = 4;
|
||||
p[1] = 0x16;
|
||||
if (page_control == 1) { /* Changeable Values */
|
||||
return p[1] + 2;
|
||||
}
|
||||
/* if a geometry hint is available, use it */
|
||||
bdrv_get_geometry_hint(bdrv, &cylinders, &heads, &secs);
|
||||
p[2] = (cylinders >> 16) & 0xff;
|
||||
@ -520,11 +530,14 @@ static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p)
|
||||
/* Medium rotation rate [rpm], 5400 rpm */
|
||||
p[20] = (5400 >> 8) & 0xff;
|
||||
p[21] = 5400 & 0xff;
|
||||
return 0x16;
|
||||
return p[1] + 2;
|
||||
|
||||
case 5: /* Flexible disk device geometry page. */
|
||||
p[0] = 5;
|
||||
p[1] = 0x1e;
|
||||
if (page_control == 1) { /* Changeable Values */
|
||||
return p[1] + 2;
|
||||
}
|
||||
/* Transfer rate [kbit/s], 5Mbit/s */
|
||||
p[2] = 5000 >> 8;
|
||||
p[3] = 5000 & 0xff;
|
||||
@ -556,21 +569,27 @@ static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p)
|
||||
/* Medium rotation rate [rpm], 5400 rpm */
|
||||
p[28] = (5400 >> 8) & 0xff;
|
||||
p[29] = 5400 & 0xff;
|
||||
return 0x1e;
|
||||
return p[1] + 2;
|
||||
|
||||
case 8: /* Caching page. */
|
||||
p[0] = 8;
|
||||
p[1] = 0x12;
|
||||
if (page_control == 1) { /* Changeable Values */
|
||||
return p[1] + 2;
|
||||
}
|
||||
if (bdrv_enable_write_cache(s->bs)) {
|
||||
p[2] = 4; /* WCE */
|
||||
}
|
||||
return 20;
|
||||
return p[1] + 2;
|
||||
|
||||
case 0x2a: /* CD Capabilities and Mechanical Status page. */
|
||||
if (bdrv_get_type_hint(bdrv) != BDRV_TYPE_CDROM)
|
||||
return 0;
|
||||
p[0] = 0x2a;
|
||||
p[1] = 0x14;
|
||||
if (page_control == 1) { /* Changeable Values */
|
||||
return p[1] + 2;
|
||||
}
|
||||
p[2] = 3; // CD-R & CD-RW read
|
||||
p[3] = 0; // Writing not supported
|
||||
p[4] = 0x7f; /* Audio, composite, digital out,
|
||||
@ -594,7 +613,7 @@ static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p)
|
||||
p[19] = (16 * 176) & 0xff;
|
||||
p[20] = (16 * 176) >> 8; // 16x write speed current
|
||||
p[21] = (16 * 176) & 0xff;
|
||||
return 22;
|
||||
return p[1] + 2;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
@ -605,29 +624,46 @@ static int scsi_disk_emulate_mode_sense(SCSIRequest *req, uint8_t *outbuf)
|
||||
{
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
|
||||
uint64_t nb_sectors;
|
||||
int page, dbd, buflen;
|
||||
int page, dbd, buflen, page_control;
|
||||
uint8_t *p;
|
||||
uint8_t dev_specific_param;
|
||||
|
||||
dbd = req->cmd.buf[1] & 0x8;
|
||||
page = req->cmd.buf[2] & 0x3f;
|
||||
DPRINTF("Mode Sense (page %d, len %zd)\n", page, req->cmd.xfer);
|
||||
page_control = (req->cmd.buf[2] & 0xc0) >> 6;
|
||||
DPRINTF("Mode Sense(%d) (page %d, xfer %zd, page_control %d)\n",
|
||||
(req->cmd.buf[0] == MODE_SENSE) ? 6 : 10, page, req->cmd.xfer, page_control);
|
||||
memset(outbuf, 0, req->cmd.xfer);
|
||||
p = outbuf;
|
||||
|
||||
p[1] = 0; /* Default media type. */
|
||||
p[3] = 0; /* Block descriptor length. */
|
||||
if (bdrv_is_read_only(s->bs)) {
|
||||
p[2] = 0x80; /* Readonly. */
|
||||
dev_specific_param = 0x80; /* Readonly. */
|
||||
} else {
|
||||
dev_specific_param = 0x00;
|
||||
}
|
||||
|
||||
if (req->cmd.buf[0] == MODE_SENSE) {
|
||||
p[1] = 0; /* Default media type. */
|
||||
p[2] = dev_specific_param;
|
||||
p[3] = 0; /* Block descriptor length. */
|
||||
p += 4;
|
||||
} else { /* MODE_SENSE_10 */
|
||||
p[2] = 0; /* Default media type. */
|
||||
p[3] = dev_specific_param;
|
||||
p[6] = p[7] = 0; /* Block descriptor length. */
|
||||
p += 8;
|
||||
}
|
||||
|
||||
bdrv_get_geometry(s->bs, &nb_sectors);
|
||||
if ((~dbd) & nb_sectors) {
|
||||
if (!dbd && nb_sectors) {
|
||||
if (req->cmd.buf[0] == MODE_SENSE) {
|
||||
outbuf[3] = 8; /* Block descriptor length */
|
||||
} else { /* MODE_SENSE_10 */
|
||||
outbuf[7] = 8; /* Block descriptor length */
|
||||
}
|
||||
nb_sectors /= s->cluster_size;
|
||||
nb_sectors--;
|
||||
if (nb_sectors > 0xffffff)
|
||||
nb_sectors = 0xffffff;
|
||||
nb_sectors = 0;
|
||||
p[0] = 0; /* media density code */
|
||||
p[1] = (nb_sectors >> 16) & 0xff;
|
||||
p[2] = (nb_sectors >> 8) & 0xff;
|
||||
@ -639,21 +675,37 @@ static int scsi_disk_emulate_mode_sense(SCSIRequest *req, uint8_t *outbuf)
|
||||
p += 8;
|
||||
}
|
||||
|
||||
if (page_control == 3) { /* Saved Values */
|
||||
return -1; /* ILLEGAL_REQUEST */
|
||||
}
|
||||
|
||||
switch (page) {
|
||||
case 0x04:
|
||||
case 0x05:
|
||||
case 0x08:
|
||||
case 0x2a:
|
||||
p += mode_sense_page(req, page, p);
|
||||
p += mode_sense_page(req, page, p, page_control);
|
||||
break;
|
||||
case 0x3f:
|
||||
p += mode_sense_page(req, 0x08, p);
|
||||
p += mode_sense_page(req, 0x2a, p);
|
||||
p += mode_sense_page(req, 0x08, p, page_control);
|
||||
p += mode_sense_page(req, 0x2a, p, page_control);
|
||||
break;
|
||||
default:
|
||||
return -1; /* ILLEGAL_REQUEST */
|
||||
}
|
||||
|
||||
buflen = p - outbuf;
|
||||
outbuf[0] = buflen - 4;
|
||||
/*
|
||||
* The mode data length field specifies the length in bytes of the
|
||||
* following data that is available to be transferred. The mode data
|
||||
* length does not include itself.
|
||||
*/
|
||||
if (req->cmd.buf[0] == MODE_SENSE) {
|
||||
outbuf[0] = buflen - 1;
|
||||
} else { /* MODE_SENSE_10 */
|
||||
outbuf[0] = ((buflen - 2) >> 8) & 0xff;
|
||||
outbuf[1] = (buflen - 2) & 0xff;
|
||||
}
|
||||
if (buflen > req->cmd.xfer)
|
||||
buflen = req->cmd.xfer;
|
||||
return buflen;
|
||||
@ -840,6 +892,12 @@ static int scsi_disk_emulate_command(SCSIRequest *req, uint8_t *outbuf)
|
||||
break;
|
||||
case VERIFY:
|
||||
break;
|
||||
case REZERO_UNIT:
|
||||
DPRINTF("Rezero Unit\n");
|
||||
if (!bdrv_is_inserted(s->bs)) {
|
||||
goto not_ready;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
goto illegal_request;
|
||||
}
|
||||
@ -959,6 +1017,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
|
||||
case SERVICE_ACTION_IN:
|
||||
case REPORT_LUNS:
|
||||
case VERIFY:
|
||||
case REZERO_UNIT:
|
||||
rc = scsi_disk_emulate_command(&r->req, outbuf);
|
||||
if (rc > 0) {
|
||||
r->iov.iov_len = rc;
|
||||
@ -982,13 +1041,40 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
|
||||
case WRITE_10:
|
||||
case WRITE_12:
|
||||
case WRITE_16:
|
||||
DPRINTF("Write (sector %" PRId64 ", count %d)\n", lba, len);
|
||||
case WRITE_VERIFY:
|
||||
case WRITE_VERIFY_12:
|
||||
case WRITE_VERIFY_16:
|
||||
DPRINTF("Write %s(sector %" PRId64 ", count %d)\n",
|
||||
(command & 0xe) == 0xe ? "And Verify " : "", lba, len);
|
||||
if (lba > s->max_lba)
|
||||
goto illegal_lba;
|
||||
r->sector = lba * s->cluster_size;
|
||||
r->sector_count = len * s->cluster_size;
|
||||
is_write = 1;
|
||||
break;
|
||||
case MODE_SELECT:
|
||||
DPRINTF("Mode Select(6) (len %d)\n", len);
|
||||
/* We don't support mode parameter changes.
|
||||
Allow the mode parameter header + block descriptors only. */
|
||||
if (len > 12) {
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case MODE_SELECT_10:
|
||||
DPRINTF("Mode Select(10) (len %d)\n", len);
|
||||
/* We don't support mode parameter changes.
|
||||
Allow the mode parameter header + block descriptors only. */
|
||||
if (len > 16) {
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case SEEK_6:
|
||||
case SEEK_10:
|
||||
DPRINTF("Seek(%d) (sector %" PRId64 ")\n", command == SEEK_6 ? 6 : 10, lba);
|
||||
if (lba > s->max_lba) {
|
||||
goto illegal_lba;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
|
||||
fail:
|
||||
|
@ -164,7 +164,7 @@ static void scsi_read_complete(void * opaque, int ret)
|
||||
int len;
|
||||
|
||||
if (ret) {
|
||||
DPRINTF("IO error\n");
|
||||
DPRINTF("IO error ret %d\n", ret);
|
||||
scsi_command_complete(r, ret);
|
||||
return;
|
||||
}
|
||||
@ -236,7 +236,7 @@ static void scsi_write_complete(void * opaque, int ret)
|
||||
if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 &&
|
||||
s->qdev.type == TYPE_TAPE) {
|
||||
s->qdev.blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11];
|
||||
DPRINTF("block size %d\n", s->blocksize);
|
||||
DPRINTF("block size %d\n", s->qdev.blocksize);
|
||||
}
|
||||
|
||||
scsi_command_complete(r, ret);
|
||||
@ -351,8 +351,18 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
|
||||
}
|
||||
scsi_req_fixup(&r->req);
|
||||
|
||||
DPRINTF("Command: lun=%d tag=0x%x data=0x%02x len %d\n", lun, tag,
|
||||
cmd[0], r->req.cmd.xfer);
|
||||
DPRINTF("Command: lun=%d tag=0x%x len %zd data=0x%02x", lun, tag,
|
||||
r->req.cmd.xfer, cmd[0]);
|
||||
|
||||
#ifdef DEBUG_SCSI
|
||||
{
|
||||
int i;
|
||||
for (i = 1; i < r->req.cmd.len; i++) {
|
||||
printf(" 0x%02x", cmd[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (r->req.cmd.xfer == 0) {
|
||||
if (r->buf != NULL)
|
||||
|
@ -481,6 +481,11 @@ static int virtio_blk_load(QEMUFile *f, void *opaque, int version_id)
|
||||
qemu_get_buffer(f, (unsigned char*)&req->elem, sizeof(req->elem));
|
||||
req->next = s->rq;
|
||||
s->rq = req;
|
||||
|
||||
virtqueue_map_sg(req->elem.in_sg, req->elem.in_addr,
|
||||
req->elem.in_num, 1);
|
||||
virtqueue_map_sg(req->elem.out_sg, req->elem.out_addr,
|
||||
req->elem.out_num, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
40
hw/virtio.c
40
hw/virtio.c
@ -360,11 +360,26 @@ int virtqueue_avail_bytes(VirtQueue *vq, int in_bytes, int out_bytes)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void virtqueue_map_sg(struct iovec *sg, target_phys_addr_t *addr,
|
||||
size_t num_sg, int is_write)
|
||||
{
|
||||
unsigned int i;
|
||||
target_phys_addr_t len;
|
||||
|
||||
for (i = 0; i < num_sg; i++) {
|
||||
len = sg[i].iov_len;
|
||||
sg[i].iov_base = cpu_physical_memory_map(addr[i], &len, is_write);
|
||||
if (sg[i].iov_base == NULL || len != sg[i].iov_len) {
|
||||
fprintf(stderr, "virtio: trying to map MMIO memory\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem)
|
||||
{
|
||||
unsigned int i, head, max;
|
||||
target_phys_addr_t desc_pa = vq->vring.desc;
|
||||
target_phys_addr_t len;
|
||||
|
||||
if (!virtqueue_num_heads(vq, vq->last_avail_idx))
|
||||
return 0;
|
||||
@ -388,29 +403,20 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem)
|
||||
i = 0;
|
||||
}
|
||||
|
||||
/* Collect all the descriptors */
|
||||
do {
|
||||
struct iovec *sg;
|
||||
int is_write = 0;
|
||||
|
||||
if (vring_desc_flags(desc_pa, i) & VRING_DESC_F_WRITE) {
|
||||
elem->in_addr[elem->in_num] = vring_desc_addr(desc_pa, i);
|
||||
sg = &elem->in_sg[elem->in_num++];
|
||||
is_write = 1;
|
||||
} else
|
||||
} else {
|
||||
elem->out_addr[elem->out_num] = vring_desc_addr(desc_pa, i);
|
||||
sg = &elem->out_sg[elem->out_num++];
|
||||
|
||||
/* Grab the first descriptor, and check it's OK. */
|
||||
sg->iov_len = vring_desc_len(desc_pa, i);
|
||||
len = sg->iov_len;
|
||||
|
||||
sg->iov_base = cpu_physical_memory_map(vring_desc_addr(desc_pa, i),
|
||||
&len, is_write);
|
||||
|
||||
if (sg->iov_base == NULL || len != sg->iov_len) {
|
||||
fprintf(stderr, "virtio: trying to map MMIO memory\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
sg->iov_len = vring_desc_len(desc_pa, i);
|
||||
|
||||
/* If we've got too many, that implies a descriptor loop. */
|
||||
if ((elem->in_num + elem->out_num) > max) {
|
||||
fprintf(stderr, "Looped descriptor");
|
||||
@ -418,6 +424,10 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem)
|
||||
}
|
||||
} while ((i = virtqueue_next_desc(desc_pa, i, max)) != max);
|
||||
|
||||
/* Now map what we have collected */
|
||||
virtqueue_map_sg(elem->in_sg, elem->in_addr, elem->in_num, 1);
|
||||
virtqueue_map_sg(elem->out_sg, elem->out_addr, elem->out_num, 0);
|
||||
|
||||
elem->index = head;
|
||||
|
||||
vq->inuse++;
|
||||
|
@ -81,6 +81,7 @@ typedef struct VirtQueueElement
|
||||
unsigned int out_num;
|
||||
unsigned int in_num;
|
||||
target_phys_addr_t in_addr[VIRTQUEUE_MAX_SIZE];
|
||||
target_phys_addr_t out_addr[VIRTQUEUE_MAX_SIZE];
|
||||
struct iovec in_sg[VIRTQUEUE_MAX_SIZE];
|
||||
struct iovec out_sg[VIRTQUEUE_MAX_SIZE];
|
||||
} VirtQueueElement;
|
||||
@ -142,6 +143,8 @@ void virtqueue_flush(VirtQueue *vq, unsigned int count);
|
||||
void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem,
|
||||
unsigned int len, unsigned int idx);
|
||||
|
||||
void virtqueue_map_sg(struct iovec *sg, target_phys_addr_t *addr,
|
||||
size_t num_sg, int is_write);
|
||||
int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem);
|
||||
int virtqueue_avail_bytes(VirtQueue *vq, int in_bytes, int out_bytes);
|
||||
|
||||
|
114
nbd.c
114
nbd.c
@ -62,6 +62,8 @@
|
||||
#define NBD_SET_SIZE_BLOCKS _IO(0xab, 7)
|
||||
#define NBD_DISCONNECT _IO(0xab, 8)
|
||||
|
||||
#define NBD_OPT_EXPORT_NAME (1 << 0)
|
||||
|
||||
/* That's all folks */
|
||||
|
||||
#define read_sync(fd, buffer, size) nbd_wr_sync(fd, buffer, size, true)
|
||||
@ -296,22 +298,27 @@ int nbd_negotiate(int csock, off_t size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nbd_receive_negotiate(int csock, off_t *size, size_t *blocksize)
|
||||
int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags,
|
||||
off_t *size, size_t *blocksize)
|
||||
{
|
||||
char buf[8 + 8 + 8 + 128];
|
||||
uint64_t magic;
|
||||
char buf[256];
|
||||
uint64_t magic, s;
|
||||
uint16_t tmp;
|
||||
|
||||
TRACE("Receiving negotation.");
|
||||
|
||||
if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) {
|
||||
if (read_sync(csock, buf, 8) != 8) {
|
||||
LOG("read failed");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
magic = be64_to_cpup((uint64_t*)(buf + 8));
|
||||
*size = be64_to_cpup((uint64_t*)(buf + 16));
|
||||
*blocksize = 1024;
|
||||
buf[8] = '\0';
|
||||
if (strlen(buf) == 0) {
|
||||
LOG("server connection closed");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
TRACE("Magic is %c%c%c%c%c%c%c%c",
|
||||
qemu_isprint(buf[0]) ? buf[0] : '.',
|
||||
@ -322,8 +329,6 @@ int nbd_receive_negotiate(int csock, off_t *size, size_t *blocksize)
|
||||
qemu_isprint(buf[5]) ? buf[5] : '.',
|
||||
qemu_isprint(buf[6]) ? buf[6] : '.',
|
||||
qemu_isprint(buf[7]) ? buf[7] : '.');
|
||||
TRACE("Magic is 0x%" PRIx64, magic);
|
||||
TRACE("Size is %" PRIu64, *size);
|
||||
|
||||
if (memcmp(buf, "NBDMAGIC", 8) != 0) {
|
||||
LOG("Invalid magic received");
|
||||
@ -331,13 +336,102 @@ int nbd_receive_negotiate(int csock, off_t *size, size_t *blocksize)
|
||||
return -1;
|
||||
}
|
||||
|
||||
TRACE("Checking magic");
|
||||
if (read_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) {
|
||||
LOG("read failed");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
magic = be64_to_cpu(magic);
|
||||
TRACE("Magic is 0x%" PRIx64, magic);
|
||||
|
||||
if (name) {
|
||||
uint32_t reserved = 0;
|
||||
uint32_t opt;
|
||||
uint32_t namesize;
|
||||
|
||||
TRACE("Checking magic (opts_magic)");
|
||||
if (magic != 0x49484156454F5054LL) {
|
||||
LOG("Bad magic received");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) {
|
||||
LOG("flags read failed");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
*flags = be16_to_cpu(tmp) << 16;
|
||||
/* reserved for future use */
|
||||
if (write_sync(csock, &reserved, sizeof(reserved)) !=
|
||||
sizeof(reserved)) {
|
||||
LOG("write failed (reserved)");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
/* write the export name */
|
||||
magic = cpu_to_be64(magic);
|
||||
if (write_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) {
|
||||
LOG("write failed (magic)");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
opt = cpu_to_be32(NBD_OPT_EXPORT_NAME);
|
||||
if (write_sync(csock, &opt, sizeof(opt)) != sizeof(opt)) {
|
||||
LOG("write failed (opt)");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
namesize = cpu_to_be32(strlen(name));
|
||||
if (write_sync(csock, &namesize, sizeof(namesize)) !=
|
||||
sizeof(namesize)) {
|
||||
LOG("write failed (namesize)");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (write_sync(csock, (char*)name, strlen(name)) != strlen(name)) {
|
||||
LOG("write failed (name)");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
TRACE("Checking magic (cli_magic)");
|
||||
|
||||
if (magic != 0x00420281861253LL) {
|
||||
LOG("Bad magic received");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (read_sync(csock, &s, sizeof(s)) != sizeof(s)) {
|
||||
LOG("read failed");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
*size = be64_to_cpu(s);
|
||||
*blocksize = 1024;
|
||||
TRACE("Size is %" PRIu64, *size);
|
||||
|
||||
if (!name) {
|
||||
if (read_sync(csock, flags, sizeof(*flags)) != sizeof(*flags)) {
|
||||
LOG("read failed (flags)");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
*flags = be32_to_cpup(flags);
|
||||
} else {
|
||||
if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) {
|
||||
LOG("read failed (tmp)");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
*flags |= be32_to_cpu(tmp);
|
||||
}
|
||||
if (read_sync(csock, &buf, 124) != 124) {
|
||||
LOG("read failed (buf)");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
5
nbd.h
5
nbd.h
@ -42,6 +42,8 @@ enum {
|
||||
NBD_CMD_DISC = 2
|
||||
};
|
||||
|
||||
#define NBD_DEFAULT_PORT 10809
|
||||
|
||||
size_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read);
|
||||
int tcp_socket_outgoing(const char *address, uint16_t port);
|
||||
int tcp_socket_incoming(const char *address, uint16_t port);
|
||||
@ -49,7 +51,8 @@ int unix_socket_outgoing(const char *path);
|
||||
int unix_socket_incoming(const char *path);
|
||||
|
||||
int nbd_negotiate(int csock, off_t size);
|
||||
int nbd_receive_negotiate(int csock, off_t *size, size_t *blocksize);
|
||||
int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags,
|
||||
off_t *size, size_t *blocksize);
|
||||
int nbd_init(int fd, int csock, off_t size, size_t blocksize);
|
||||
int nbd_send_request(int csock, struct nbd_request *request);
|
||||
int nbd_receive_reply(int csock, struct nbd_reply *reply);
|
||||
|
@ -599,6 +599,7 @@ BlockDriverAIOCB *paio_ioctl(BlockDriverState *bs, int fd,
|
||||
acb->aio_type = QEMU_AIO_IOCTL;
|
||||
acb->aio_fildes = fd;
|
||||
acb->ev_signo = SIGUSR2;
|
||||
acb->async_context_id = get_async_context_id();
|
||||
acb->aio_offset = 0;
|
||||
acb->aio_ioctl_buf = buf;
|
||||
acb->aio_ioctl_cmd = req;
|
||||
|
@ -620,6 +620,13 @@ qemu linux1.img -hdb nbd:unix:/tmp/my_socket
|
||||
qemu linux2.img -hdb nbd:unix:/tmp/my_socket
|
||||
@end example
|
||||
|
||||
If the nbd-server uses named exports (since NBD 2.9.18), you must use the
|
||||
"exportname" option:
|
||||
@example
|
||||
qemu -cdrom nbd:localhost:exportname=debian-500-ppc-netinst
|
||||
qemu -cdrom nbd:localhost:exportname=openSUSE-11.1-ppc-netinst
|
||||
@end example
|
||||
|
||||
@node pcsys_network
|
||||
@section Network emulation
|
||||
|
||||
|
@ -783,7 +783,8 @@ static int img_convert(int argc, char **argv)
|
||||
goto out;
|
||||
}
|
||||
|
||||
out_bs = bdrv_new_open(out_filename, out_fmt, BDRV_O_FLAGS | BDRV_O_RDWR);
|
||||
out_bs = bdrv_new_open(out_filename, out_fmt,
|
||||
BDRV_O_FLAGS | BDRV_O_RDWR | BDRV_O_NO_FLUSH);
|
||||
if (!out_bs) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
@ -1286,7 +1287,7 @@ static int img_rebase(int argc, char **argv)
|
||||
}
|
||||
|
||||
bs_new_backing = bdrv_new("new_backing");
|
||||
ret = bdrv_open(bs_new_backing, out_baseimg, BDRV_O_FLAGS | BDRV_O_RDWR,
|
||||
ret = bdrv_open(bs_new_backing, out_baseimg, BDRV_O_FLAGS,
|
||||
new_backing_drv);
|
||||
if (ret) {
|
||||
error("Could not open new backing file '%s'", out_baseimg);
|
||||
|
@ -1427,9 +1427,6 @@ alloc_f(int argc, char **argv)
|
||||
|
||||
cvtstr(offset, s1, sizeof(s1));
|
||||
|
||||
if (nb_sectors == 1)
|
||||
printf("sector allocated at offset %s\n", s1);
|
||||
else
|
||||
printf("%d/%d sectors allocated at offset %s\n",
|
||||
sum_alloc, nb_sectors, s1);
|
||||
return 0;
|
||||
|
@ -230,6 +230,7 @@ int main(int argc, char **argv)
|
||||
int nb_fds = 0;
|
||||
int max_fd;
|
||||
int persistent = 0;
|
||||
uint32_t nbdflags;
|
||||
|
||||
while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
|
||||
switch (ch) {
|
||||
@ -398,7 +399,8 @@ int main(int argc, char **argv)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = nbd_receive_negotiate(sock, &size, &blocksize);
|
||||
ret = nbd_receive_negotiate(sock, NULL, &nbdflags,
|
||||
&size, &blocksize);
|
||||
if (ret == -1) {
|
||||
ret = 1;
|
||||
goto out;
|
||||
|
86
savevm.c
86
savevm.c
@ -1837,8 +1837,10 @@ void do_savevm(Monitor *mon, const QDict *qdict)
|
||||
uint32_t vm_state_size;
|
||||
#ifdef _WIN32
|
||||
struct _timeb tb;
|
||||
struct tm *ptm;
|
||||
#else
|
||||
struct timeval tv;
|
||||
struct tm tm;
|
||||
#endif
|
||||
const char *name = qdict_get_try_str(qdict, "name");
|
||||
|
||||
@ -1869,15 +1871,6 @@ void do_savevm(Monitor *mon, const QDict *qdict)
|
||||
vm_stop(0);
|
||||
|
||||
memset(sn, 0, sizeof(*sn));
|
||||
if (name) {
|
||||
ret = bdrv_snapshot_find(bs, old_sn, name);
|
||||
if (ret >= 0) {
|
||||
pstrcpy(sn->name, sizeof(sn->name), old_sn->name);
|
||||
pstrcpy(sn->id_str, sizeof(sn->id_str), old_sn->id_str);
|
||||
} else {
|
||||
pstrcpy(sn->name, sizeof(sn->name), name);
|
||||
}
|
||||
}
|
||||
|
||||
/* fill auxiliary fields */
|
||||
#ifdef _WIN32
|
||||
@ -1891,6 +1884,24 @@ void do_savevm(Monitor *mon, const QDict *qdict)
|
||||
#endif
|
||||
sn->vm_clock_nsec = qemu_get_clock(vm_clock);
|
||||
|
||||
if (name) {
|
||||
ret = bdrv_snapshot_find(bs, old_sn, name);
|
||||
if (ret >= 0) {
|
||||
pstrcpy(sn->name, sizeof(sn->name), old_sn->name);
|
||||
pstrcpy(sn->id_str, sizeof(sn->id_str), old_sn->id_str);
|
||||
} else {
|
||||
pstrcpy(sn->name, sizeof(sn->name), name);
|
||||
}
|
||||
} else {
|
||||
#ifdef _WIN32
|
||||
ptm = localtime(&tb.time);
|
||||
strftime(sn->name, sizeof(sn->name), "vm-%Y%m%d%H%M%S", ptm);
|
||||
#else
|
||||
localtime_r(&tv.tv_sec, &tm);
|
||||
strftime(sn->name, sizeof(sn->name), "vm-%Y%m%d%H%M%S", &tm);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Delete old snapshots of the same name */
|
||||
if (name && del_existing_snapshots(mon, name) < 0) {
|
||||
goto the_end;
|
||||
@ -2039,8 +2050,10 @@ void do_delvm(Monitor *mon, const QDict *qdict)
|
||||
void do_info_snapshots(Monitor *mon)
|
||||
{
|
||||
BlockDriverState *bs, *bs1;
|
||||
QEMUSnapshotInfo *sn_tab, *sn;
|
||||
int nb_sns, i;
|
||||
QEMUSnapshotInfo *sn_tab, *sn, s, *sn_info = &s;
|
||||
int nb_sns, i, ret, available;
|
||||
int total;
|
||||
int *available_snapshots;
|
||||
char buf[256];
|
||||
|
||||
bs = bdrv_snapshots();
|
||||
@ -2048,27 +2061,52 @@ void do_info_snapshots(Monitor *mon)
|
||||
monitor_printf(mon, "No available block device supports snapshots\n");
|
||||
return;
|
||||
}
|
||||
monitor_printf(mon, "Snapshot devices:");
|
||||
bs1 = NULL;
|
||||
while ((bs1 = bdrv_next(bs1))) {
|
||||
if (bdrv_can_snapshot(bs1)) {
|
||||
if (bs == bs1)
|
||||
monitor_printf(mon, " %s", bdrv_get_device_name(bs1));
|
||||
}
|
||||
}
|
||||
monitor_printf(mon, "\n");
|
||||
|
||||
nb_sns = bdrv_snapshot_list(bs, &sn_tab);
|
||||
if (nb_sns < 0) {
|
||||
monitor_printf(mon, "bdrv_snapshot_list: error %d\n", nb_sns);
|
||||
return;
|
||||
}
|
||||
monitor_printf(mon, "Snapshot list (from %s):\n",
|
||||
bdrv_get_device_name(bs));
|
||||
monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL));
|
||||
|
||||
if (nb_sns == 0) {
|
||||
monitor_printf(mon, "There is no snapshot available.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
available_snapshots = qemu_mallocz(sizeof(int) * nb_sns);
|
||||
total = 0;
|
||||
for (i = 0; i < nb_sns; i++) {
|
||||
sn = &sn_tab[i];
|
||||
available = 1;
|
||||
bs1 = NULL;
|
||||
|
||||
while ((bs1 = bdrv_next(bs1))) {
|
||||
if (bdrv_can_snapshot(bs1) && bs1 != bs) {
|
||||
ret = bdrv_snapshot_find(bs1, sn_info, sn->id_str);
|
||||
if (ret < 0) {
|
||||
available = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (available) {
|
||||
available_snapshots[total] = i;
|
||||
total++;
|
||||
}
|
||||
}
|
||||
|
||||
if (total > 0) {
|
||||
monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL));
|
||||
for (i = 0; i < total; i++) {
|
||||
sn = &sn_tab[available_snapshots[i]];
|
||||
monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn));
|
||||
}
|
||||
qemu_free(sn_tab);
|
||||
} else {
|
||||
monitor_printf(mon, "There is no suitable snapshot available\n");
|
||||
}
|
||||
|
||||
qemu_free(sn_tab);
|
||||
qemu_free(available_snapshots);
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user