aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/e1000.c11
-rw-r--r--hw/file-op-9p.h24
-rw-r--r--hw/ide/core.c4
-rw-r--r--hw/lsi53c895a.c4
-rw-r--r--hw/s390-virtio-bus.c8
-rw-r--r--hw/s390-virtio-bus.h1
-rw-r--r--hw/scsi-disk.c130
-rw-r--r--hw/scsi-generic.c18
-rw-r--r--hw/syborg_virtio.c8
-rw-r--r--hw/vhost_net.c24
-rw-r--r--hw/virtio-9p-debug.c147
-rw-r--r--hw/virtio-9p-debug.h1
-rw-r--r--hw/virtio-9p-local.c168
-rw-r--r--hw/virtio-9p.c1698
-rw-r--r--hw/virtio-9p.h196
-rw-r--r--hw/virtio-blk.c5
-rw-r--r--hw/virtio-net.c129
-rw-r--r--hw/virtio-net.h14
-rw-r--r--hw/virtio-pci.c8
-rw-r--r--hw/virtio.c38
-rw-r--r--hw/virtio.h7
21 files changed, 2316 insertions, 327 deletions
diff --git a/hw/e1000.c b/hw/e1000.c
index 80b78bc618..7d7d14002f 100644
--- a/hw/e1000.c
+++ b/hw/e1000.c
@@ -345,7 +345,7 @@ is_vlan_txd(uint32_t txd_lower)
/* FCS aka Ethernet CRC-32. We don't get it from backends and can't
* fill it in, just pad descriptor length by 4 bytes unless guest
- * told us to trip it off the packet. */
+ * told us to strip it off the packet. */
static inline int
fcs_len(E1000State *s)
{
@@ -690,9 +690,14 @@ e1000_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
s->mac_reg[GPRC]++;
s->mac_reg[TPR]++;
- n = s->mac_reg[TORL];
- if ((s->mac_reg[TORL] += size) < n)
+ /* TOR - Total Octets Received:
+ * This register includes bytes received in a packet from the <Destination
+ * Address> field through the <CRC> field, inclusively.
+ */
+ n = s->mac_reg[TORL] + size + /* Always include FCS length. */ 4;
+ if (n < s->mac_reg[TORL])
s->mac_reg[TORH]++;
+ s->mac_reg[TORL] = n;
n = E1000_ICS_RXT0;
if ((rdt = s->mac_reg[RDT]) < s->mac_reg[RDH])
diff --git a/hw/file-op-9p.h b/hw/file-op-9p.h
index a741c93527..d91b7e7996 100644
--- a/hw/file-op-9p.h
+++ b/hw/file-op-9p.h
@@ -24,8 +24,19 @@
typedef enum
{
- SM_PASSTHROUGH = 1, /* uid/gid set on fileserver files */
- SM_MAPPED, /* uid/gid part of xattr */
+ /*
+ * Server will try to set uid/gid.
+ * On failure ignore the error.
+ */
+ SM_NONE = 0,
+ /*
+ * uid/gid set on fileserver files
+ */
+ SM_PASSTHROUGH = 1,
+ /*
+ * uid/gid part of xattr
+ */
+ SM_MAPPED,
} SecModel;
typedef struct FsCred
@@ -52,7 +63,7 @@ typedef struct FileOperations
int (*chmod)(FsContext *, const char *, FsCred *);
int (*chown)(FsContext *, const char *, FsCred *);
int (*mknod)(FsContext *, const char *, FsCred *);
- int (*utime)(FsContext *, const char *, const struct utimbuf *);
+ int (*utimensat)(FsContext *, const char *, const struct timespec *);
int (*remove)(FsContext *, const char *);
int (*symlink)(FsContext *, const char *, const char *, FsCred *);
int (*link)(FsContext *, const char *, const char *);
@@ -74,6 +85,13 @@ typedef struct FileOperations
int (*rename)(FsContext *, const char *, const char *);
int (*truncate)(FsContext *, const char *, off_t);
int (*fsync)(FsContext *, int);
+ int (*statfs)(FsContext *s, const char *path, struct statfs *stbuf);
+ ssize_t (*lgetxattr)(FsContext *, const char *,
+ const char *, void *, size_t);
+ ssize_t (*llistxattr)(FsContext *, const char *, void *, size_t);
+ int (*lsetxattr)(FsContext *, const char *,
+ const char *, void *, size_t, int);
+ int (*lremovexattr)(FsContext *, const char *, const char *);
void *opaque;
} FileOperations;
#endif
diff --git a/hw/ide/core.c b/hw/ide/core.c
index 3651d2be91..1e466d1ff5 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.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 */
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
index bd7b661426..5eaf69ed3f 100644
--- a/hw/lsi53c895a.c
+++ b/hw/lsi53c895a.c
@@ -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");
diff --git a/hw/s390-virtio-bus.c b/hw/s390-virtio-bus.c
index fe6884d47d..784dc01b97 100644
--- a/hw/s390-virtio-bus.c
+++ b/hw/s390-virtio-bus.c
@@ -27,6 +27,7 @@
#include "elf.h"
#include "hw/virtio.h"
#include "hw/virtio-serial.h"
+#include "hw/virtio-net.h"
#include "hw/sysbus.h"
#include "kvm.h"
@@ -110,7 +111,7 @@ static int s390_virtio_net_init(VirtIOS390Device *dev)
{
VirtIODevice *vdev;
- vdev = virtio_net_init((DeviceState *)dev, &dev->nic);
+ vdev = virtio_net_init((DeviceState *)dev, &dev->nic, &dev->net);
if (!vdev) {
return -1;
}
@@ -327,6 +328,11 @@ static VirtIOS390DeviceInfo s390_virtio_net = {
.qdev.size = sizeof(VirtIOS390Device),
.qdev.props = (Property[]) {
DEFINE_NIC_PROPERTIES(VirtIOS390Device, nic),
+ DEFINE_PROP_UINT32("x-txtimer", VirtIOS390Device,
+ net.txtimer, TX_TIMER_INTERVAL),
+ DEFINE_PROP_INT32("x-txburst", VirtIOS390Device,
+ net.txburst, TX_BURST),
+ DEFINE_PROP_STRING("tx", VirtIOS390Device, net.tx),
DEFINE_PROP_END_OF_LIST(),
},
};
diff --git a/hw/s390-virtio-bus.h b/hw/s390-virtio-bus.h
index 333fea8963..41558c9c67 100644
--- a/hw/s390-virtio-bus.h
+++ b/hw/s390-virtio-bus.h
@@ -43,6 +43,7 @@ typedef struct VirtIOS390Device {
uint32_t host_features;
/* Max. number of ports we can have for a the virtio-serial device */
uint32_t max_virtserial_ports;
+ virtio_net_conf net;
} VirtIOS390Device;
typedef struct VirtIOS390Bus {
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 07a6d86946..1446ca634c 100644
--- a/hw/scsi-disk.c
+++ b/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;
}
- p += 4;
bdrv_get_geometry(s->bs, &nb_sectors);
- if ((~dbd) & nb_sectors) {
- outbuf[3] = 8; /* Block descriptor length */
+ 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:
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index aa4f62ae57..953802777d 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -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)
diff --git a/hw/syborg_virtio.c b/hw/syborg_virtio.c
index abf0370107..4dfd1a87b9 100644
--- a/hw/syborg_virtio.c
+++ b/hw/syborg_virtio.c
@@ -68,6 +68,7 @@ typedef struct {
uint32_t id;
NICConf nic;
uint32_t host_features;
+ virtio_net_conf net;
} SyborgVirtIOProxy;
static uint32_t syborg_virtio_readl(void *opaque, target_phys_addr_t offset)
@@ -284,7 +285,7 @@ static int syborg_virtio_net_init(SysBusDevice *dev)
VirtIODevice *vdev;
SyborgVirtIOProxy *proxy = FROM_SYSBUS(SyborgVirtIOProxy, dev);
- vdev = virtio_net_init(&dev->qdev, &proxy->nic);
+ vdev = virtio_net_init(&dev->qdev, &proxy->nic, &proxy->net);
return syborg_virtio_init(proxy, vdev);
}
@@ -295,6 +296,11 @@ static SysBusDeviceInfo syborg_virtio_net_info = {
.qdev.props = (Property[]) {
DEFINE_NIC_PROPERTIES(SyborgVirtIOProxy, nic),
DEFINE_VIRTIO_NET_FEATURES(SyborgVirtIOProxy, host_features),
+ DEFINE_PROP_UINT32("x-txtimer", SyborgVirtIOProxy,
+ net.txtimer, TX_TIMER_INTERVAL),
+ DEFINE_PROP_INT32("x-txburst", SyborgVirtIOProxy,
+ net.txburst, TX_BURST),
+ DEFINE_PROP_STRING("tx", SyborgVirtIOProxy, net.tx),
DEFINE_PROP_END_OF_LIST(),
}
};
diff --git a/hw/vhost_net.c b/hw/vhost_net.c
index 0c00de272a..4a7b8194f2 100644
--- a/hw/vhost_net.c
+++ b/hw/vhost_net.c
@@ -50,7 +50,9 @@ unsigned vhost_net_get_features(struct vhost_net *net, unsigned features)
if (!(net->dev.features & (1 << VIRTIO_RING_F_INDIRECT_DESC))) {
features &= ~(1 << VIRTIO_RING_F_INDIRECT_DESC);
}
- features &= ~(1 << VIRTIO_NET_F_MRG_RXBUF);
+ if (!(net->dev.features & (1 << VIRTIO_NET_F_MRG_RXBUF))) {
+ features &= ~(1 << VIRTIO_NET_F_MRG_RXBUF);
+ }
return features;
}
@@ -63,6 +65,9 @@ void vhost_net_ack_features(struct vhost_net *net, unsigned features)
if (features & (1 << VIRTIO_RING_F_INDIRECT_DESC)) {
net->dev.acked_features |= (1 << VIRTIO_RING_F_INDIRECT_DESC);
}
+ if (features & (1 << VIRTIO_NET_F_MRG_RXBUF)) {
+ net->dev.acked_features |= (1 << VIRTIO_NET_F_MRG_RXBUF);
+ }
}
static int vhost_net_get_fd(VLANClientState *backend)
@@ -97,6 +102,10 @@ struct vhost_net *vhost_net_init(VLANClientState *backend, int devfd)
if (r < 0) {
goto fail;
}
+ if (!tap_has_vnet_hdr_len(backend,
+ sizeof(struct virtio_net_hdr_mrg_rxbuf))) {
+ net->dev.features &= ~(1 << VIRTIO_NET_F_MRG_RXBUF);
+ }
if (~net->dev.features & net->dev.backend_features) {
fprintf(stderr, "vhost lacks feature mask %" PRIu64 " for backend\n",
(uint64_t)(~net->dev.features & net->dev.backend_features));
@@ -117,6 +126,10 @@ int vhost_net_start(struct vhost_net *net,
{
struct vhost_vring_file file = { };
int r;
+ if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) {
+ tap_set_vnet_hdr_len(net->vc,
+ sizeof(struct virtio_net_hdr_mrg_rxbuf));
+ }
net->dev.nvqs = 2;
net->dev.vqs = net->vqs;
@@ -144,6 +157,9 @@ fail:
}
net->vc->info->poll(net->vc, true);
vhost_dev_stop(&net->dev, dev);
+ if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) {
+ tap_set_vnet_hdr_len(net->vc, sizeof(struct virtio_net_hdr));
+ }
return r;
}
@@ -158,11 +174,17 @@ void vhost_net_stop(struct vhost_net *net,
}
net->vc->info->poll(net->vc, true);
vhost_dev_stop(&net->dev, dev);
+ if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) {
+ tap_set_vnet_hdr_len(net->vc, sizeof(struct virtio_net_hdr));
+ }
}
void vhost_net_cleanup(struct vhost_net *net)
{
vhost_dev_cleanup(&net->dev);
+ if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) {
+ tap_set_vnet_hdr_len(net->vc, sizeof(struct virtio_net_hdr));
+ }
qemu_free(net);
}
#else
diff --git a/hw/virtio-9p-debug.c b/hw/virtio-9p-debug.c
index c1b0e6f066..6f6a0ec13b 100644
--- a/hw/virtio-9p-debug.c
+++ b/hw/virtio-9p-debug.c
@@ -169,15 +169,37 @@ static void pprint_stat(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
pprint_str(pdu, rx, offsetp, ", uid");
pprint_str(pdu, rx, offsetp, ", gid");
pprint_str(pdu, rx, offsetp, ", muid");
- if (dotu) {
- pprint_str(pdu, rx, offsetp, ", extension");
- pprint_int32(pdu, rx, offsetp, ", uid");
- pprint_int32(pdu, rx, offsetp, ", gid");
- pprint_int32(pdu, rx, offsetp, ", muid");
- }
+ pprint_str(pdu, rx, offsetp, ", extension");
+ pprint_int32(pdu, rx, offsetp, ", uid");
+ pprint_int32(pdu, rx, offsetp, ", gid");
+ pprint_int32(pdu, rx, offsetp, ", muid");
+ fprintf(llogfile, "}");
+}
+
+static void pprint_stat_dotl(V9fsPDU *pdu, int rx, size_t *offsetp,
+ const char *name)
+{
+ fprintf(llogfile, "%s={", name);
+ pprint_qid(pdu, rx, offsetp, "qid");
+ pprint_int32(pdu, rx, offsetp, ", st_mode");
+ pprint_int64(pdu, rx, offsetp, ", st_nlink");
+ pprint_int32(pdu, rx, offsetp, ", st_uid");
+ pprint_int32(pdu, rx, offsetp, ", st_gid");
+ pprint_int64(pdu, rx, offsetp, ", st_rdev");
+ pprint_int64(pdu, rx, offsetp, ", st_size");
+ pprint_int64(pdu, rx, offsetp, ", st_blksize");
+ pprint_int64(pdu, rx, offsetp, ", st_blocks");
+ pprint_int64(pdu, rx, offsetp, ", atime");
+ pprint_int64(pdu, rx, offsetp, ", atime_nsec");
+ pprint_int64(pdu, rx, offsetp, ", mtime");
+ pprint_int64(pdu, rx, offsetp, ", mtime_nsec");
+ pprint_int64(pdu, rx, offsetp, ", ctime");
+ pprint_int64(pdu, rx, offsetp, ", ctime_nsec");
fprintf(llogfile, "}");
}
+
+
static void pprint_strs(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
{
int sg_count = get_sg_count(pdu, rx);
@@ -330,6 +352,30 @@ void pprint_pdu(V9fsPDU *pdu)
BUG_ON(!llogfile);
switch (pdu->id) {
+ case P9_TREADDIR:
+ fprintf(llogfile, "TREADDIR: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_int64(pdu, 0, &offset, ", initial offset");
+ pprint_int32(pdu, 0, &offset, ", max count");
+ break;
+ case P9_RREADDIR:
+ fprintf(llogfile, "RREADDIR: (");
+ pprint_int32(pdu, 1, &offset, "count");
+#ifdef DEBUG_DATA
+ pprint_data(pdu, 1, &offset, ", data");
+#endif
+ break;
+ case P9_TMKDIR:
+ fprintf(llogfile, "TMKDIR: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_str(pdu, 0, &offset, "name");
+ pprint_int32(pdu, 0, &offset, "mode");
+ pprint_int32(pdu, 0, &offset, "gid");
+ break;
+ case P9_RMKDIR:
+ fprintf(llogfile, "RMKDIR: (");
+ pprint_qid(pdu, 0, &offset, "qid");
+ break;
case P9_TVERSION:
fprintf(llogfile, "TVERSION: (");
pprint_int32(pdu, 0, &offset, "msize");
@@ -340,14 +386,20 @@ void pprint_pdu(V9fsPDU *pdu)
pprint_int32(pdu, 1, &offset, "msize");
pprint_str(pdu, 1, &offset, ", version");
break;
+ case P9_TGETATTR:
+ fprintf(llogfile, "TGETATTR: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ break;
+ case P9_RGETATTR:
+ fprintf(llogfile, "RGETATTR: (");
+ pprint_stat_dotl(pdu, 1, &offset, "getattr");
+ break;
case P9_TAUTH:
fprintf(llogfile, "TAUTH: (");
pprint_int32(pdu, 0, &offset, "afid");
pprint_str(pdu, 0, &offset, ", uname");
pprint_str(pdu, 0, &offset, ", aname");
- if (dotu) {
- pprint_int32(pdu, 0, &offset, ", n_uname");
- }
+ pprint_int32(pdu, 0, &offset, ", n_uname");
break;
case P9_RAUTH:
fprintf(llogfile, "RAUTH: (");
@@ -359,9 +411,7 @@ void pprint_pdu(V9fsPDU *pdu)
pprint_int32(pdu, 0, &offset, ", afid");
pprint_str(pdu, 0, &offset, ", uname");
pprint_str(pdu, 0, &offset, ", aname");
- if (dotu) {
- pprint_int32(pdu, 0, &offset, ", n_uname");
- }
+ pprint_int32(pdu, 0, &offset, ", n_uname");
break;
case P9_RATTACH:
fprintf(llogfile, "RATTACH: (");
@@ -373,9 +423,7 @@ void pprint_pdu(V9fsPDU *pdu)
case P9_RERROR:
fprintf(llogfile, "RERROR: (");
pprint_str(pdu, 1, &offset, "ename");
- if (dotu) {
- pprint_int32(pdu, 1, &offset, ", ecode");
- }
+ pprint_int32(pdu, 1, &offset, ", ecode");
break;
case P9_TFLUSH:
fprintf(llogfile, "TFLUSH: (");
@@ -410,15 +458,50 @@ void pprint_pdu(V9fsPDU *pdu)
pprint_str(pdu, 0, &offset, ", name");
pprint_int32(pdu, 0, &offset, ", perm");
pprint_int8(pdu, 0, &offset, ", mode");
- if (dotu) {
- pprint_str(pdu, 0, &offset, ", extension");
- }
+ pprint_str(pdu, 0, &offset, ", extension");
break;
case P9_RCREATE:
fprintf(llogfile, "RCREATE: (");
pprint_qid(pdu, 1, &offset, "qid");
pprint_int32(pdu, 1, &offset, ", iounit");
break;
+ case P9_TSYMLINK:
+ fprintf(llogfile, "TSYMLINK: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_str(pdu, 0, &offset, ", name");
+ pprint_str(pdu, 0, &offset, ", symname");
+ pprint_int32(pdu, 0, &offset, ", gid");
+ break;
+ case P9_RSYMLINK:
+ fprintf(llogfile, "RSYMLINK: (");
+ pprint_qid(pdu, 1, &offset, "qid");
+ break;
+ case P9_TLCREATE:
+ fprintf(llogfile, "TLCREATE: (");
+ pprint_int32(pdu, 0, &offset, "dfid");
+ pprint_str(pdu, 0, &offset, ", name");
+ pprint_int32(pdu, 0, &offset, ", flags");
+ pprint_int32(pdu, 0, &offset, ", mode");
+ pprint_int32(pdu, 0, &offset, ", gid");
+ break;
+ case P9_RLCREATE:
+ fprintf(llogfile, "RLCREATE: (");
+ pprint_qid(pdu, 1, &offset, "qid");
+ pprint_int32(pdu, 1, &offset, ", iounit");
+ break;
+ case P9_TMKNOD:
+ fprintf(llogfile, "TMKNOD: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_str(pdu, 0, &offset, "name");
+ pprint_int32(pdu, 0, &offset, "mode");
+ pprint_int32(pdu, 0, &offset, "major");
+ pprint_int32(pdu, 0, &offset, "minor");
+ pprint_int32(pdu, 0, &offset, "gid");
+ break;
+ case P9_RMKNOD:
+ fprintf(llogfile, "RMKNOD: )");
+ pprint_qid(pdu, 0, &offset, "qid");
+ break;
case P9_TREAD:
fprintf(llogfile, "TREAD: (");
pprint_int32(pdu, 0, &offset, "fid");
@@ -452,6 +535,15 @@ void pprint_pdu(V9fsPDU *pdu)
case P9_RCLUNK:
fprintf(llogfile, "RCLUNK: (");
break;
+ case P9_TLINK:
+ fprintf(llogfile, "TLINK: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_str(pdu, 0, &offset, ", oldpath");
+ pprint_str(pdu, 0, &offset, ", newpath");
+ break;
+ case P9_RLINK:
+ fprintf(llogfile, "RLINK: (");
+ break;
case P9_TREMOVE:
fprintf(llogfile, "TREMOVE: (");
pprint_int32(pdu, 0, &offset, "fid");
@@ -477,6 +569,25 @@ void pprint_pdu(V9fsPDU *pdu)
case P9_RWSTAT:
fprintf(llogfile, "RWSTAT: (");
break;
+ case P9_TXATTRWALK:
+ fprintf(llogfile, "TXATTRWALK: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_int32(pdu, 0, &offset, ", newfid");
+ pprint_str(pdu, 0, &offset, ", xattr name");
+ break;
+ case P9_RXATTRWALK:
+ fprintf(llogfile, "RXATTRWALK: (");
+ pprint_int64(pdu, 1, &offset, "xattrsize");
+ case P9_TXATTRCREATE:
+ fprintf(llogfile, "TXATTRCREATE: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_str(pdu, 0, &offset, ", name");
+ pprint_int64(pdu, 0, &offset, ", xattrsize");
+ pprint_int32(pdu, 0, &offset, ", flags");
+ break;
+ case P9_RXATTRCREATE:
+ fprintf(llogfile, "RXATTRCREATE: (");
+ break;
default:
fprintf(llogfile, "unknown(%d): (", pdu->id);
break;
diff --git a/hw/virtio-9p-debug.h b/hw/virtio-9p-debug.h
index 0104be5eb3..d9a249118d 100644
--- a/hw/virtio-9p-debug.h
+++ b/hw/virtio-9p-debug.h
@@ -1,7 +1,6 @@
#ifndef _QEMU_VIRTIO_9P_DEBUG_H
#define _QEMU_VIRTIO_9P_DEBUG_H
-extern int dotu;
void pprint_pdu(V9fsPDU *pdu);
#endif
diff --git a/hw/virtio-9p-local.c b/hw/virtio-9p-local.c
index 43c03c188f..57f92433d3 100644
--- a/hw/virtio-9p-local.c
+++ b/hw/virtio-9p-local.c
@@ -101,8 +101,14 @@ static int local_post_create_passthrough(FsContext *fs_ctx, const char *path,
if (chmod(rpath(fs_ctx, path), credp->fc_mode & 07777) < 0) {
return -1;
}
- if (chown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid) < 0) {
- return -1;
+ if (lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid) < 0) {
+ /*
+ * If we fail to change ownership and if we are
+ * using security model none. Ignore the error
+ */
+ if (fs_ctx->fs_sm != SM_NONE) {
+ return -1;
+ }
}
return 0;
}
@@ -122,7 +128,8 @@ static ssize_t local_readlink(FsContext *fs_ctx, const char *path,
} while (tsize == -1 && errno == EINTR);
close(fd);
return tsize;
- } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
+ } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+ (fs_ctx->fs_sm == SM_NONE)) {
tsize = readlink(rpath(fs_ctx, path), buf, bufsz);
}
return tsize;
@@ -189,7 +196,8 @@ static int local_chmod(FsContext *fs_ctx, const char *path, FsCred *credp)
{
if (fs_ctx->fs_sm == SM_MAPPED) {
return local_set_xattr(rpath(fs_ctx, path), credp);
- } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
+ } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+ (fs_ctx->fs_sm == SM_NONE)) {
return chmod(rpath(fs_ctx, path), credp->fc_mode);
}
return -1;
@@ -211,7 +219,8 @@ static int local_mknod(FsContext *fs_ctx, const char *path, FsCred *credp)
serrno = errno;
goto err_end;
}
- } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
+ } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+ (fs_ctx->fs_sm == SM_NONE)) {
err = mknod(rpath(fs_ctx, path), credp->fc_mode, credp->fc_rdev);
if (err == -1) {
return err;
@@ -247,7 +256,8 @@ static int local_mkdir(FsContext *fs_ctx, const char *path, FsCred *credp)
serrno = errno;
goto err_end;
}
- } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
+ } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+ (fs_ctx->fs_sm == SM_NONE)) {
err = mkdir(rpath(fs_ctx, path), credp->fc_mode);
if (err == -1) {
return err;
@@ -316,7 +326,8 @@ static int local_open2(FsContext *fs_ctx, const char *path, int flags,
serrno = errno;
goto err_end;
}
- } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
+ } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+ (fs_ctx->fs_sm == SM_NONE)) {
fd = open(rpath(fs_ctx, path), flags, credp->fc_mode);
if (fd == -1) {
return fd;
@@ -372,15 +383,23 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath,
serrno = errno;
goto err_end;
}
- } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
+ } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+ (fs_ctx->fs_sm == SM_NONE)) {
err = symlink(oldpath, rpath(fs_ctx, newpath));
if (err) {
return err;
}
err = lchown(rpath(fs_ctx, newpath), credp->fc_uid, credp->fc_gid);
if (err == -1) {
- serrno = errno;
- goto err_end;
+ /*
+ * If we fail to change ownership and if we are
+ * using security model none. Ignore the error
+ */
+ if (fs_ctx->fs_sm != SM_NONE) {
+ serrno = errno;
+ goto err_end;
+ } else
+ err = 0;
}
}
return err;
@@ -442,18 +461,22 @@ static int local_rename(FsContext *ctx, const char *oldpath,
static int local_chown(FsContext *fs_ctx, const char *path, FsCred *credp)
{
- if (fs_ctx->fs_sm == SM_MAPPED) {
+ if ((credp->fc_uid == -1 && credp->fc_gid == -1) ||
+ (fs_ctx->fs_sm == SM_PASSTHROUGH)) {
+ return lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid);
+ } else if (fs_ctx->fs_sm == SM_MAPPED) {
return local_set_xattr(rpath(fs_ctx, path), credp);
- } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
+ } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+ (fs_ctx->fs_sm == SM_NONE)) {
return lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid);
}
return -1;
}
-static int local_utime(FsContext *ctx, const char *path,
- const struct utimbuf *buf)
+static int local_utimensat(FsContext *s, const char *path,
+ const struct timespec *buf)
{
- return utime(rpath(ctx, path), buf);
+ return utimensat(AT_FDCWD, rpath(s, path), buf, AT_SYMLINK_NOFOLLOW);
}
static int local_remove(FsContext *ctx, const char *path)
@@ -466,6 +489,114 @@ static int local_fsync(FsContext *ctx, int fd)
return fsync(fd);
}
+static int local_statfs(FsContext *s, const char *path, struct statfs *stbuf)
+{
+ return statfs(rpath(s, path), stbuf);
+}
+
+static ssize_t local_lgetxattr(FsContext *ctx, const char *path,
+ const char *name, void *value, size_t size)
+{
+ if ((ctx->fs_sm == SM_MAPPED) &&
+ (strncmp(name, "user.virtfs.", 12) == 0)) {
+ /*
+ * Don't allow fetch of user.virtfs namesapce
+ * in case of mapped security
+ */
+ errno = ENOATTR;
+ return -1;
+ }
+
+ return lgetxattr(rpath(ctx, path), name, value, size);
+}
+
+static ssize_t local_llistxattr(FsContext *ctx, const char *path,
+ void *value, size_t size)
+{
+ ssize_t retval;
+ ssize_t actual_len = 0;
+ char *orig_value, *orig_value_start;
+ char *temp_value, *temp_value_start;
+ ssize_t xattr_len, parsed_len = 0, attr_len;
+
+ if (ctx->fs_sm != SM_MAPPED) {
+ return llistxattr(rpath(ctx, path), value, size);
+ }
+
+ /* Get the actual len */
+ xattr_len = llistxattr(rpath(ctx, path), value, 0);
+
+ /* Now fetch the xattr and find the actual size */
+ orig_value = qemu_malloc(xattr_len);
+ xattr_len = llistxattr(rpath(ctx, path), orig_value, xattr_len);
+
+ /*
+ * For mapped security model drop user.virtfs namespace
+ * from the list
+ */
+ temp_value = qemu_mallocz(xattr_len);
+ temp_value_start = temp_value;
+ orig_value_start = orig_value;
+ while (xattr_len > parsed_len) {
+ attr_len = strlen(orig_value) + 1;
+ if (strncmp(orig_value, "user.virtfs.", 12) != 0) {
+ /* Copy this entry */
+ strcat(temp_value, orig_value);
+ temp_value += attr_len;
+ actual_len += attr_len;
+ }
+ parsed_len += attr_len;
+ orig_value += attr_len;
+ }
+ if (!size) {
+ retval = actual_len;
+ goto out;
+ } else if (size >= actual_len) {
+ /* now copy the parsed attribute list back */
+ memset(value, 0, size);
+ memcpy(value, temp_value_start, actual_len);
+ retval = actual_len;
+ goto out;
+ }
+ errno = ERANGE;
+ retval = -1;
+out:
+ qemu_free(orig_value_start);
+ qemu_free(temp_value_start);
+ return retval;
+}
+
+static int local_lsetxattr(FsContext *ctx, const char *path, const char *name,
+ void *value, size_t size, int flags)
+{
+ if ((ctx->fs_sm == SM_MAPPED) &&
+ (strncmp(name, "user.virtfs.", 12) == 0)) {
+ /*
+ * Don't allow fetch of user.virtfs namesapce
+ * in case of mapped security
+ */
+ errno = EACCES;
+ return -1;
+ }
+ return lsetxattr(rpath(ctx, path), name, value, size, flags);
+}
+
+static int local_lremovexattr(FsContext *ctx,
+ const char *path, const char *name)
+{
+ if ((ctx->fs_sm == SM_MAPPED) &&
+ (strncmp(name, "user.virtfs.", 12) == 0)) {
+ /*
+ * Don't allow fetch of user.virtfs namesapce
+ * in case of mapped security
+ */
+ errno = EACCES;
+ return -1;
+ }
+ return lremovexattr(rpath(ctx, path), name);
+}
+
+
FileOperations local_ops = {
.lstat = local_lstat,
.readlink = local_readlink,
@@ -490,7 +621,12 @@ FileOperations local_ops = {
.truncate = local_truncate,
.rename = local_rename,
.chown = local_chown,
- .utime = local_utime,
+ .utimensat = local_utimensat,
.remove = local_remove,
.fsync = local_fsync,
+ .statfs = local_statfs,
+ .lgetxattr = local_lgetxattr,
+ .llistxattr = local_llistxattr,
+ .lsetxattr = local_lsetxattr,
+ .lremovexattr = local_lremovexattr,
};
diff --git a/hw/virtio-9p.c b/hw/virtio-9p.c
index 047c7ea4eb..32fa3bcc5c 100644
--- a/hw/virtio-9p.c
+++ b/hw/virtio-9p.c
@@ -18,7 +18,6 @@
#include "fsdev/qemu-fsdev.h"
#include "virtio-9p-debug.h"
-int dotu = 1;
int debug_9p_pdu;
enum {
@@ -160,26 +159,29 @@ static int v9fs_do_chmod(V9fsState *s, V9fsString *path, mode_t mode)
return s->ops->chmod(&s->ctx, path->data, &cred);
}
-static int v9fs_do_mknod(V9fsState *s, V9fsCreateState *vs, mode_t mode,
- dev_t dev)
+static int v9fs_do_mknod(V9fsState *s, char *name,
+ mode_t mode, dev_t dev, uid_t uid, gid_t gid)
{
FsCred cred;
cred_init(&cred);
- cred.fc_uid = vs->fidp->uid;
+ cred.fc_uid = uid;
+ cred.fc_gid = gid;
cred.fc_mode = mode;
cred.fc_rdev = dev;
- return s->ops->mknod(&s->ctx, vs->fullname.data, &cred);
+ return s->ops->mknod(&s->ctx, name, &cred);
}
-static int v9fs_do_mkdir(V9fsState *s, V9fsCreateState *vs)
+static int v9fs_do_mkdir(V9fsState *s, char *name, mode_t mode,
+ uid_t uid, gid_t gid)
{
FsCred cred;
cred_init(&cred);
- cred.fc_uid = vs->fidp->uid;
- cred.fc_mode = vs->perm & 0777;
+ cred.fc_uid = uid;
+ cred.fc_gid = gid;
+ cred.fc_mode = mode;
- return s->ops->mkdir(&s->ctx, vs->fullname.data, &cred);
+ return s->ops->mkdir(&s->ctx, name, &cred);
}
static int v9fs_do_fstat(V9fsState *s, int fd, struct stat *stbuf)
@@ -187,28 +189,30 @@ static int v9fs_do_fstat(V9fsState *s, int fd, struct stat *stbuf)
return s->ops->fstat(&s->ctx, fd, stbuf);
}
-static int v9fs_do_open2(V9fsState *s, V9fsCreateState *vs)
+static int v9fs_do_open2(V9fsState *s, char *fullname, uid_t uid, gid_t gid,
+ int flags, int mode)
{
FsCred cred;
- int flags;
cred_init(&cred);
- cred.fc_uid = vs->fidp->uid;
- cred.fc_mode = vs->perm & 0777;
- flags = omode_to_uflags(vs->mode) | O_CREAT;
+ cred.fc_uid = uid;
+ cred.fc_gid = gid;
+ cred.fc_mode = mode & 07777;
+ flags = flags;
- return s->ops->open2(&s->ctx, vs->fullname.data, flags, &cred);
+ return s->ops->open2(&s->ctx, fullname, flags, &cred);
}
-static int v9fs_do_symlink(V9fsState *s, V9fsCreateState *vs)
+static int v9fs_do_symlink(V9fsState *s, V9fsFidState *fidp,
+ const char *oldpath, const char *newpath, gid_t gid)
{
FsCred cred;
cred_init(&cred);
- cred.fc_uid = vs->fidp->uid;
- cred.fc_mode = vs->perm | 0777;
+ cred.fc_uid = fidp->uid;
+ cred.fc_gid = gid;
+ cred.fc_mode = 0777;
- return s->ops->symlink(&s->ctx, vs->extension.data, vs->fullname.data,
- &cred);
+ return s->ops->symlink(&s->ctx, oldpath, newpath, &cred);
}
static int v9fs_do_link(V9fsState *s, V9fsString *oldpath, V9fsString *newpath)
@@ -237,10 +241,10 @@ static int v9fs_do_chown(V9fsState *s, V9fsString *path, uid_t uid, gid_t gid)
return s->ops->chown(&s->ctx, path->data, &cred);
}
-static int v9fs_do_utime(V9fsState *s, V9fsString *path,
- const struct utimbuf *buf)
+static int v9fs_do_utimensat(V9fsState *s, V9fsString *path,
+ const struct timespec times[2])
{
- return s->ops->utime(&s->ctx, path->data, buf);
+ return s->ops->utimensat(&s->ctx, path->data, times);
}
static int v9fs_do_remove(V9fsState *s, V9fsString *path)
@@ -253,6 +257,42 @@ static int v9fs_do_fsync(V9fsState *s, int fd)
return s->ops->fsync(&s->ctx, fd);
}
+static int v9fs_do_statfs(V9fsState *s, V9fsString *path, struct statfs *stbuf)
+{
+ return s->ops->statfs(&s->ctx, path->data, stbuf);
+}
+
+static ssize_t v9fs_do_lgetxattr(V9fsState *s, V9fsString *path,
+ V9fsString *xattr_name,
+ void *value, size_t size)
+{
+ return s->ops->lgetxattr(&s->ctx, path->data,
+ xattr_name->data, value, size);
+}
+
+static ssize_t v9fs_do_llistxattr(V9fsState *s, V9fsString *path,
+ void *value, size_t size)
+{
+ return s->ops->llistxattr(&s->ctx, path->data,
+ value, size);
+}
+
+static int v9fs_do_lsetxattr(V9fsState *s, V9fsString *path,
+ V9fsString *xattr_name,
+ void *value, size_t size, int flags)
+{
+ return s->ops->lsetxattr(&s->ctx, path->data,
+ xattr_name->data, value, size, flags);
+}
+
+static int v9fs_do_lremovexattr(V9fsState *s, V9fsString *path,
+ V9fsString *xattr_name)
+{
+ return s->ops->lremovexattr(&s->ctx, path->data,
+ xattr_name->data);
+}
+
+
static void v9fs_string_init(V9fsString *str)
{
str->data = NULL;
@@ -398,8 +438,7 @@ static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid)
f = qemu_mallocz(sizeof(V9fsFidState));
f->fid = fid;
- f->fd = -1;
- f->dir = NULL;
+ f->fid_type = P9_FID_NONE;
f->next = s->fid_list;
s->fid_list = f;
@@ -407,8 +446,43 @@ static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid)
return f;
}
+static int v9fs_xattr_fid_clunk(V9fsState *s, V9fsFidState *fidp)
+{
+ int retval = 0;
+
+ if (fidp->fs.xattr.copied_len == -1) {
+ /* getxattr/listxattr fid */
+ goto free_value;
+ }
+ /*
+ * if this is fid for setxattr. clunk should
+ * result in setxattr localcall
+ */
+ if (fidp->fs.xattr.len != fidp->fs.xattr.copied_len) {
+ /* clunk after partial write */
+ retval = -EINVAL;
+ goto free_out;
+ }
+ if (fidp->fs.xattr.len) {
+ retval = v9fs_do_lsetxattr(s, &fidp->path, &fidp->fs.xattr.name,
+ fidp->fs.xattr.value,
+ fidp->fs.xattr.len,
+ fidp->fs.xattr.flags);
+ } else {
+ retval = v9fs_do_lremovexattr(s, &fidp->path, &fidp->fs.xattr.name);
+ }
+free_out:
+ v9fs_string_free(&fidp->fs.xattr.name);
+free_value:
+ if (fidp->fs.xattr.value) {
+ qemu_free(fidp->fs.xattr.value);
+ }
+ return retval;
+}
+
static int free_fid(V9fsState *s, int32_t fid)
{
+ int retval = 0;
V9fsFidState **fidpp, *fidp;
for (fidpp = &s->fid_list; *fidpp; fidpp = &(*fidpp)->next) {
@@ -424,16 +498,17 @@ static int free_fid(V9fsState *s, int32_t fid)
fidp = *fidpp;
*fidpp = fidp->next;
- if (fidp->fd != -1) {
- v9fs_do_close(s, fidp->fd);
- }
- if (fidp->dir) {
- v9fs_do_closedir(s, fidp->dir);
+ if (fidp->fid_type == P9_FID_FILE) {
+ v9fs_do_close(s, fidp->fs.fd);
+ } else if (fidp->fid_type == P9_FID_DIR) {
+ v9fs_do_closedir(s, fidp->fs.dir);
+ } else if (fidp->fid_type == P9_FID_XATTR) {
+ retval = v9fs_xattr_fid_clunk(s, fidp);
}
v9fs_string_free(&fidp->path);
qemu_free(fidp);
- return 0;
+ return retval;
}
#define P9_QID_TYPE_DIR 0x80
@@ -660,6 +735,15 @@ static size_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
&statp->n_muid);
break;
}
+ case 'I': {
+ V9fsIattr *iattr = va_arg(ap, V9fsIattr *);
+ offset += pdu_unmarshal(pdu, offset, "ddddqqqqq",
+ &iattr->valid, &iattr->mode,
+ &iattr->uid, &iattr->gid, &iattr->size,
+ &iattr->atime_sec, &iattr->atime_nsec,
+ &iattr->mtime_sec, &iattr->mtime_nsec);
+ break;
+ }
default:
break;
}
@@ -731,6 +815,21 @@ static size_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
statp->n_gid, statp->n_muid);
break;
}
+ case 'A': {
+ V9fsStatDotl *statp = va_arg(ap, V9fsStatDotl *);
+ offset += pdu_marshal(pdu, offset, "qQdddqqqqqqqqqqqqqqq",
+ statp->st_result_mask,
+ &statp->qid, statp->st_mode,
+ statp->st_uid, statp->st_gid,
+ statp->st_nlink, statp->st_rdev,
+ statp->st_size, statp->st_blksize, statp->st_blocks,
+ statp->st_atime_sec, statp->st_atime_nsec,
+ statp->st_mtime_sec, statp->st_mtime_nsec,
+ statp->st_ctime_sec, statp->st_ctime_nsec,
+ statp->st_btime_sec, statp->st_btime_nsec,
+ statp->st_gen, statp->st_data_version);
+ break;
+ }
default:
break;
}
@@ -745,19 +844,24 @@ static void complete_pdu(V9fsState *s, V9fsPDU *pdu, ssize_t len)
int8_t id = pdu->id + 1; /* Response */
if (len < 0) {
- V9fsString str;
int err = -len;
+ len = 7;
- str.data = strerror(err);
- str.size = strlen(str.data);
+ if (s->proto_version != V9FS_PROTO_2000L) {
+ V9fsString str;
- len = 7;
- len += pdu_marshal(pdu, len, "s", &str);
- if (dotu) {
- len += pdu_marshal(pdu, len, "d", err);
+ str.data = strerror(err);
+ str.size = strlen(str.data);
+
+ len += pdu_marshal(pdu, len, "s", &str);
+ id = P9_RERROR;
}
- id = P9_RERROR;
+ len += pdu_marshal(pdu, len, "d", err);
+
+ if (s->proto_version == V9FS_PROTO_2000L) {
+ id = P9_RLERROR;
+ }
}
/* fill out the header */
@@ -785,22 +889,20 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension)
ret |= S_IFDIR;
}
- if (dotu) {
- if (mode & P9_STAT_MODE_SYMLINK) {
- ret |= S_IFLNK;
- }
- if (mode & P9_STAT_MODE_SOCKET) {
- ret |= S_IFSOCK;
- }
- if (mode & P9_STAT_MODE_NAMED_PIPE) {
- ret |= S_IFIFO;
- }
- if (mode & P9_STAT_MODE_DEVICE) {
- if (extension && extension->data[0] == 'c') {
- ret |= S_IFCHR;
- } else {
- ret |= S_IFBLK;
- }
+ if (mode & P9_STAT_MODE_SYMLINK) {
+ ret |= S_IFLNK;
+ }
+ if (mode & P9_STAT_MODE_SOCKET) {
+ ret |= S_IFSOCK;
+ }
+ if (mode & P9_STAT_MODE_NAMED_PIPE) {
+ ret |= S_IFIFO;
+ }
+ if (mode & P9_STAT_MODE_DEVICE) {
+ if (extension && extension->data[0] == 'c') {
+ ret |= S_IFCHR;
+ } else {
+ ret |= S_IFBLK;
}
}
@@ -863,34 +965,32 @@ static uint32_t stat_to_v9mode(const struct stat *stbuf)
mode |= P9_STAT_MODE_DIR;
}
- if (dotu) {
- if (S_ISLNK(stbuf->st_mode)) {
- mode |= P9_STAT_MODE_SYMLINK;
- }
+ if (S_ISLNK(stbuf->st_mode)) {
+ mode |= P9_STAT_MODE_SYMLINK;
+ }
- if (S_ISSOCK(stbuf->st_mode)) {
- mode |= P9_STAT_MODE_SOCKET;
- }
+ if (S_ISSOCK(stbuf->st_mode)) {
+ mode |= P9_STAT_MODE_SOCKET;
+ }
- if (S_ISFIFO(stbuf->st_mode)) {
- mode |= P9_STAT_MODE_NAMED_PIPE;
- }
+ if (S_ISFIFO(stbuf->st_mode)) {
+ mode |= P9_STAT_MODE_NAMED_PIPE;
+ }
- if (S_ISBLK(stbuf->st_mode) || S_ISCHR(stbuf->st_mode)) {
- mode |= P9_STAT_MODE_DEVICE;
- }
+ if (S_ISBLK(stbuf->st_mode) || S_ISCHR(stbuf->st_mode)) {
+ mode |= P9_STAT_MODE_DEVICE;
+ }
- if (stbuf->st_mode & S_ISUID) {
- mode |= P9_STAT_MODE_SETUID;
- }
+ if (stbuf->st_mode & S_ISUID) {
+ mode |= P9_STAT_MODE_SETUID;
+ }
- if (stbuf->st_mode & S_ISGID) {
- mode |= P9_STAT_MODE_SETGID;
- }
+ if (stbuf->st_mode & S_ISGID) {
+ mode |= P9_STAT_MODE_SETGID;
+ }
- if (stbuf->st_mode & S_ISVTX) {
- mode |= P9_STAT_MODE_SETVTX;
- }
+ if (stbuf->st_mode & S_ISVTX) {
+ mode |= P9_STAT_MODE_SETVTX;
}
return mode;
@@ -915,29 +1015,27 @@ static int stat_to_v9stat(V9fsState *s, V9fsString *name,
v9fs_string_null(&v9stat->gid);
v9fs_string_null(&v9stat->muid);
- if (dotu) {
- v9stat->n_uid = stbuf->st_uid;
- v9stat->n_gid = stbuf->st_gid;
- v9stat->n_muid = 0;
+ v9stat->n_uid = stbuf->st_uid;
+ v9stat->n_gid = stbuf->st_gid;
+ v9stat->n_muid = 0;
- v9fs_string_null(&v9stat->extension);
+ v9fs_string_null(&v9stat->extension);
- if (v9stat->mode & P9_STAT_MODE_SYMLINK) {
- err = v9fs_do_readlink(s, name, &v9stat->extension);
- if (err == -1) {
- err = -errno;
- return err;
- }
- v9stat->extension.data[err] = 0;
- v9stat->extension.size = err;
- } else if (v9stat->mode & P9_STAT_MODE_DEVICE) {
- v9fs_string_sprintf(&v9stat->extension, "%c %u %u",
- S_ISCHR(stbuf->st_mode) ? 'c' : 'b',
- major(stbuf->st_rdev), minor(stbuf->st_rdev));
- } else if (S_ISDIR(stbuf->st_mode) || S_ISREG(stbuf->st_mode)) {
- v9fs_string_sprintf(&v9stat->extension, "%s %u",
- "HARDLINKCOUNT", stbuf->st_nlink);
+ if (v9stat->mode & P9_STAT_MODE_SYMLINK) {
+ err = v9fs_do_readlink(s, name, &v9stat->extension);
+ if (err == -1) {
+ err = -errno;
+ return err;
}
+ v9stat->extension.data[err] = 0;
+ v9stat->extension.size = err;
+ } else if (v9stat->mode & P9_STAT_MODE_DEVICE) {
+ v9fs_string_sprintf(&v9stat->extension, "%c %u %u",
+ S_ISCHR(stbuf->st_mode) ? 'c' : 'b',
+ major(stbuf->st_rdev), minor(stbuf->st_rdev));
+ } else if (S_ISDIR(stbuf->st_mode) || S_ISREG(stbuf->st_mode)) {
+ v9fs_string_sprintf(&v9stat->extension, "%s %u",
+ "HARDLINKCOUNT", stbuf->st_nlink);
}
str = strrchr(name->data, '/');
@@ -958,6 +1056,51 @@ static int stat_to_v9stat(V9fsState *s, V9fsString *name,
return 0;
}
+#define P9_STATS_MODE 0x00000001ULL
+#define P9_STATS_NLINK 0x00000002ULL
+#define P9_STATS_UID 0x00000004ULL
+#define P9_STATS_GID 0x00000008ULL
+#define P9_STATS_RDEV 0x00000010ULL
+#define P9_STATS_ATIME 0x00000020ULL
+#define P9_STATS_MTIME 0x00000040ULL
+#define P9_STATS_CTIME 0x00000080ULL
+#define P9_STATS_INO 0x00000100ULL
+#define P9_STATS_SIZE 0x00000200ULL
+#define P9_STATS_BLOCKS 0x00000400ULL
+
+#define P9_STATS_BTIME 0x00000800ULL
+#define P9_STATS_GEN 0x00001000ULL
+#define P9_STATS_DATA_VERSION 0x00002000ULL
+
+#define P9_STATS_BASIC 0x000007ffULL /* Mask for fields up to BLOCKS */
+#define P9_STATS_ALL 0x00003fffULL /* Mask for All fields above */
+
+
+static void stat_to_v9stat_dotl(V9fsState *s, const struct stat *stbuf,
+ V9fsStatDotl *v9lstat)
+{
+ memset(v9lstat, 0, sizeof(*v9lstat));
+
+ v9lstat->st_mode = stbuf->st_mode;
+ v9lstat->st_nlink = stbuf->st_nlink;
+ v9lstat->st_uid = stbuf->st_uid;
+ v9lstat->st_gid = stbuf->st_gid;
+ v9lstat->st_rdev = stbuf->st_rdev;
+ v9lstat->st_size = stbuf->st_size;
+ v9lstat->st_blksize = stbuf->st_blksize;
+ v9lstat->st_blocks = stbuf->st_blocks;
+ v9lstat->st_atime_sec = stbuf->st_atime;
+ v9lstat->st_atime_nsec = stbuf->st_atim.tv_nsec;
+ v9lstat->st_mtime_sec = stbuf->st_mtime;
+ v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec;
+ v9lstat->st_ctime_sec = stbuf->st_ctime;
+ v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec;
+ /* Currently we only support BASIC fields in stat */
+ v9lstat->st_result_mask = P9_STATS_BASIC;
+
+ stat_to_qid(stbuf, &v9lstat->qid);
+}
+
static struct iovec *adjust_sg(struct iovec *sg, int len, int *iovcnt)
{
while (len && *iovcnt) {
@@ -1019,17 +1162,20 @@ static void v9fs_fix_path(V9fsString *dst, V9fsString *src, int len)
static void v9fs_version(V9fsState *s, V9fsPDU *pdu)
{
- int32_t msize;
V9fsString version;
size_t offset = 7;
- pdu_unmarshal(pdu, offset, "ds", &msize, &version);
+ pdu_unmarshal(pdu, offset, "ds", &s->msize, &version);
- if (strcmp(version.data, "9P2000.u")) {
+ if (!strcmp(version.data, "9P2000.u")) {
+ s->proto_version = V9FS_PROTO_2000U;
+ } else if (!strcmp(version.data, "9P2000.L")) {
+ s->proto_version = V9FS_PROTO_2000L;
+ } else {
v9fs_string_sprintf(&version, "unknown");
}
- offset += pdu_marshal(pdu, offset, "ds", msize, &version);
+ offset += pdu_marshal(pdu, offset, "ds", s->msize, &version);
complete_pdu(s, pdu, offset);
v9fs_string_free(&version);
@@ -1121,6 +1267,202 @@ out:
qemu_free(vs);
}
+static void v9fs_getattr_post_lstat(V9fsState *s, V9fsStatStateDotl *vs,
+ int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ stat_to_v9stat_dotl(s, &vs->stbuf, &vs->v9stat_dotl);
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "A", &vs->v9stat_dotl);
+ err = vs->offset;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_getattr(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsStatStateDotl *vs;
+ ssize_t err = 0;
+ V9fsFidState *fidp;
+ uint64_t request_mask;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ memset(&vs->v9stat_dotl, 0, sizeof(vs->v9stat_dotl));
+
+ pdu_unmarshal(vs->pdu, vs->offset, "dq", &fid, &request_mask);
+
+ fidp = lookup_fid(s, fid);
+ if (fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ /* Currently we only support BASIC fields in stat, so there is no
+ * need to look at request_mask.
+ */
+ err = v9fs_do_lstat(s, &fidp->path, &vs->stbuf);
+ v9fs_getattr_post_lstat(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+/* From Linux kernel code */
+#define ATTR_MODE (1 << 0)
+#define ATTR_UID (1 << 1)
+#define ATTR_GID (1 << 2)
+#define ATTR_SIZE (1 << 3)
+#define ATTR_ATIME (1 << 4)
+#define ATTR_MTIME (1 << 5)
+#define ATTR_CTIME (1 << 6)
+#define ATTR_MASK 127
+#define ATTR_ATIME_SET (1 << 7)
+#define ATTR_MTIME_SET (1 << 8)
+
+static void v9fs_setattr_post_truncate(V9fsState *s, V9fsSetattrState *vs,
+ int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+ err = vs->offset;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_setattr_post_chown(V9fsState *s, V9fsSetattrState *vs, int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ if (vs->v9iattr.valid & (ATTR_SIZE)) {
+ err = v9fs_do_truncate(s, &vs->fidp->path, vs->v9iattr.size);
+ }
+ v9fs_setattr_post_truncate(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_setattr_post_utimensat(V9fsState *s, V9fsSetattrState *vs,
+ int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ /* If the only valid entry in iattr is ctime we can call
+ * chown(-1,-1) to update the ctime of the file
+ */
+ if ((vs->v9iattr.valid & (ATTR_UID | ATTR_GID)) ||
+ ((vs->v9iattr.valid & ATTR_CTIME)
+ && !((vs->v9iattr.valid & ATTR_MASK) & ~ATTR_CTIME))) {
+ if (!(vs->v9iattr.valid & ATTR_UID)) {
+ vs->v9iattr.uid = -1;
+ }
+ if (!(vs->v9iattr.valid & ATTR_GID)) {
+ vs->v9iattr.gid = -1;
+ }
+ err = v9fs_do_chown(s, &vs->fidp->path, vs->v9iattr.uid,
+ vs->v9iattr.gid);
+ }
+ v9fs_setattr_post_chown(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_setattr_post_chmod(V9fsState *s, V9fsSetattrState *vs, int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ if (vs->v9iattr.valid & (ATTR_ATIME | ATTR_MTIME)) {
+ struct timespec times[2];
+ if (vs->v9iattr.valid & ATTR_ATIME) {
+ if (vs->v9iattr.valid & ATTR_ATIME_SET) {
+ times[0].tv_sec = vs->v9iattr.atime_sec;
+ times[0].tv_nsec = vs->v9iattr.atime_nsec;
+ } else {
+ times[0].tv_nsec = UTIME_NOW;
+ }
+ } else {
+ times[0].tv_nsec = UTIME_OMIT;
+ }
+
+ if (vs->v9iattr.valid & ATTR_MTIME) {
+ if (vs->v9iattr.valid & ATTR_MTIME_SET) {
+ times[1].tv_sec = vs->v9iattr.mtime_sec;
+ times[1].tv_nsec = vs->v9iattr.mtime_nsec;
+ } else {
+ times[1].tv_nsec = UTIME_NOW;
+ }
+ } else {
+ times[1].tv_nsec = UTIME_OMIT;
+ }
+ err = v9fs_do_utimensat(s, &vs->fidp->path, times);
+ }
+ v9fs_setattr_post_utimensat(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_setattr(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsSetattrState *vs;
+ int err = 0;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ pdu_unmarshal(pdu, vs->offset, "dI", &fid, &vs->v9iattr);
+
+ vs->fidp = lookup_fid(s, fid);
+ if (vs->fidp == NULL) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (vs->v9iattr.valid & ATTR_MODE) {
+ err = v9fs_do_chmod(s, &vs->fidp->path, vs->v9iattr.mode);
+ }
+
+ v9fs_setattr_post_chmod(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
static void v9fs_walk_complete(V9fsState *s, V9fsWalkState *vs, int err)
{
complete_pdu(s, vs->pdu, err);
@@ -1241,8 +1583,7 @@ static void v9fs_walk(V9fsState *s, V9fsPDU *pdu)
/* FIXME: is this really valid? */
if (fid == newfid) {
- BUG_ON(vs->fidp->fd != -1);
- BUG_ON(vs->fidp->dir);
+ BUG_ON(vs->fidp->fid_type != P9_FID_NONE);
v9fs_string_init(&vs->path);
vs->name_idx = 0;
@@ -1284,13 +1625,33 @@ out:
v9fs_walk_complete(s, vs, err);
}
+static int32_t get_iounit(V9fsState *s, V9fsString *name)
+{
+ struct statfs stbuf;
+ int32_t iounit = 0;
+
+ /*
+ * iounit should be multiples of f_bsize (host filesystem block size
+ * and as well as less than (client msize - P9_IOHDRSZ))
+ */
+ if (!v9fs_do_statfs(s, name, &stbuf)) {
+ iounit = stbuf.f_bsize;
+ iounit *= (s->msize - P9_IOHDRSZ)/stbuf.f_bsize;
+ }
+
+ if (!iounit) {
+ iounit = s->msize - P9_IOHDRSZ;
+ }
+ return iounit;
+}
+
static void v9fs_open_post_opendir(V9fsState *s, V9fsOpenState *vs, int err)
{
- if (vs->fidp->dir == NULL) {
+ if (vs->fidp->fs.dir == NULL) {
err = -errno;
goto out;
}
-
+ vs->fidp->fid_type = P9_FID_DIR;
vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, 0);
err = vs->offset;
out:
@@ -1299,15 +1660,25 @@ out:
}
+static void v9fs_open_post_getiounit(V9fsState *s, V9fsOpenState *vs)
+{
+ int err;
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, vs->iounit);
+ err = vs->offset;
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
static void v9fs_open_post_open(V9fsState *s, V9fsOpenState *vs, int err)
{
- if (vs->fidp->fd == -1) {
+ if (vs->fidp->fs.fd == -1) {
err = -errno;
goto out;
}
-
- vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, 0);
- err = vs->offset;
+ vs->fidp->fid_type = P9_FID_FILE;
+ vs->iounit = get_iounit(s, &vs->fidp->path);
+ v9fs_open_post_getiounit(s, vs);
+ return;
out:
complete_pdu(s, vs->pdu, err);
qemu_free(vs);
@@ -1315,6 +1686,8 @@ out:
static void v9fs_open_post_lstat(V9fsState *s, V9fsOpenState *vs, int err)
{
+ int flags;
+
if (err) {
err = -errno;
goto out;
@@ -1323,11 +1696,16 @@ static void v9fs_open_post_lstat(V9fsState *s, V9fsOpenState *vs, int err)
stat_to_qid(&vs->stbuf, &vs->qid);
if (S_ISDIR(vs->stbuf.st_mode)) {
- vs->fidp->dir = v9fs_do_opendir(s, &vs->fidp->path);
+ vs->fidp->fs.dir = v9fs_do_opendir(s, &vs->fidp->path);
v9fs_open_post_opendir(s, vs, err);
} else {
- vs->fidp->fd = v9fs_do_open(s, &vs->fidp->path,
- omode_to_uflags(vs->mode));
+ if (s->proto_version == V9FS_PROTO_2000L) {
+ flags = vs->mode;
+ flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT);
+ } else {
+ flags = omode_to_uflags(vs->mode);
+ }
+ vs->fidp->fs.fd = v9fs_do_open(s, &vs->fidp->path, flags);
v9fs_open_post_open(s, vs, err);
}
return;
@@ -1342,12 +1720,16 @@ static void v9fs_open(V9fsState *s, V9fsPDU *pdu)
V9fsOpenState *vs;
ssize_t err = 0;
-
vs = qemu_malloc(sizeof(*vs));
vs->pdu = pdu;
vs->offset = 7;
+ vs->mode = 0;
- pdu_unmarshal(vs->pdu, vs->offset, "db", &fid, &vs->mode);
+ if (s->proto_version == V9FS_PROTO_2000L) {
+ pdu_unmarshal(vs->pdu, vs->offset, "dd", &fid, &vs->mode);
+ } else {
+ pdu_unmarshal(vs->pdu, vs->offset, "db", &fid, &vs->mode);
+ }
vs->fidp = lookup_fid(s, fid);
if (vs->fidp == NULL) {
@@ -1355,8 +1737,7 @@ static void v9fs_open(V9fsState *s, V9fsPDU *pdu)
goto out;
}
- BUG_ON(vs->fidp->fd != -1);
- BUG_ON(vs->fidp->dir);
+ BUG_ON(vs->fidp->fid_type != P9_FID_NONE);
err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
@@ -1367,6 +1748,91 @@ out:
qemu_free(vs);
}
+static void v9fs_post_lcreate(V9fsState *s, V9fsLcreateState *vs, int err)
+{
+ if (err == 0) {
+ v9fs_string_copy(&vs->fidp->path, &vs->fullname);
+ stat_to_qid(&vs->stbuf, &vs->qid);
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid,
+ &vs->iounit);
+ err = vs->offset;
+ } else {
+ vs->fidp->fid_type = P9_FID_NONE;
+ close(vs->fidp->fs.fd);
+ err = -errno;
+ }
+
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ v9fs_string_free(&vs->fullname);
+ qemu_free(vs);
+}
+
+static void v9fs_lcreate_post_get_iounit(V9fsState *s, V9fsLcreateState *vs,
+ int err)
+{
+ if (err) {
+ err = -errno;
+ goto out;
+ }
+ err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
+
+out:
+ v9fs_post_lcreate(s, vs, err);
+}
+
+static void v9fs_lcreate_post_do_open2(V9fsState *s, V9fsLcreateState *vs,
+ int err)
+{
+ if (vs->fidp->fs.fd == -1) {
+ err = -errno;
+ goto out;
+ }
+ vs->fidp->fid_type = P9_FID_FILE;
+ vs->iounit = get_iounit(s, &vs->fullname);
+ v9fs_lcreate_post_get_iounit(s, vs, err);
+ return;
+
+out:
+ v9fs_post_lcreate(s, vs, err);
+}
+
+static void v9fs_lcreate(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t dfid, flags, mode;
+ gid_t gid;
+ V9fsLcreateState *vs;
+ ssize_t err = 0;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ v9fs_string_init(&vs->fullname);
+
+ pdu_unmarshal(vs->pdu, vs->offset, "dsddd", &dfid, &vs->name, &flags,
+ &mode, &gid);
+
+ vs->fidp = lookup_fid(s, dfid);
+ if (vs->fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ v9fs_string_sprintf(&vs->fullname, "%s/%s", vs->fidp->path.data,
+ vs->name.data);
+
+ vs->fidp->fs.fd = v9fs_do_open2(s, vs->fullname.data, vs->fidp->uid,
+ gid, flags, mode);
+ v9fs_lcreate_post_do_open2(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
static void v9fs_clunk(V9fsState *s, V9fsPDU *pdu)
{
int32_t fid;
@@ -1420,7 +1886,7 @@ static void v9fs_read_post_dir_lstat(V9fsState *s, V9fsReadState *vs,
&vs->v9stat);
if ((vs->len != (vs->v9stat.size + 2)) ||
((vs->count + vs->len) > vs->max_count)) {
- v9fs_do_seekdir(s, vs->fidp->dir, vs->dir_pos);
+ v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->dir_pos);
v9fs_read_post_seekdir(s, vs, err);
return;
}
@@ -1428,11 +1894,11 @@ static void v9fs_read_post_dir_lstat(V9fsState *s, V9fsReadState *vs,
v9fs_stat_free(&vs->v9stat);
v9fs_string_free(&vs->name);
vs->dir_pos = vs->dent->d_off;
- vs->dent = v9fs_do_readdir(s, vs->fidp->dir);
+ vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
v9fs_read_post_readdir(s, vs, err);
return;
out:
- v9fs_do_seekdir(s, vs->fidp->dir, vs->dir_pos);
+ v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->dir_pos);
v9fs_read_post_seekdir(s, vs, err);
return;
@@ -1460,7 +1926,7 @@ static void v9fs_read_post_readdir(V9fsState *s, V9fsReadState *vs, ssize_t err)
static void v9fs_read_post_telldir(V9fsState *s, V9fsReadState *vs, ssize_t err)
{
- vs->dent = v9fs_do_readdir(s, vs->fidp->dir);
+ vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
v9fs_read_post_readdir(s, vs, err);
return;
}
@@ -1468,7 +1934,7 @@ static void v9fs_read_post_telldir(V9fsState *s, V9fsReadState *vs, ssize_t err)
static void v9fs_read_post_rewinddir(V9fsState *s, V9fsReadState *vs,
ssize_t err)
{
- vs->dir_pos = v9fs_do_telldir(s, vs->fidp->dir);
+ vs->dir_pos = v9fs_do_telldir(s, vs->fidp->fs.dir);
v9fs_read_post_telldir(s, vs, err);
return;
}
@@ -1487,7 +1953,7 @@ static void v9fs_read_post_readv(V9fsState *s, V9fsReadState *vs, ssize_t err)
if (0) {
print_sg(vs->sg, vs->cnt);
}
- vs->len = v9fs_do_readv(s, vs->fidp->fd, vs->sg, vs->cnt);
+ vs->len = v9fs_do_readv(s, vs->fidp->fs.fd, vs->sg, vs->cnt);
} while (vs->len == -1 && errno == EINTR);
if (vs->len == -1) {
err = -errno;
@@ -1517,7 +1983,7 @@ static void v9fs_read_post_lseek(V9fsState *s, V9fsReadState *vs, ssize_t err)
if (0) {
print_sg(vs->sg, vs->cnt);
}
- vs->len = v9fs_do_readv(s, vs->fidp->fd, vs->sg, vs->cnt);
+ vs->len = v9fs_do_readv(s, vs->fidp->fs.fd, vs->sg, vs->cnt);
} while (vs->len == -1 && errno == EINTR);
if (vs->len == -1) {
err = -errno;
@@ -1530,6 +1996,31 @@ out:
qemu_free(vs);
}
+static void v9fs_xattr_read(V9fsState *s, V9fsReadState *vs)
+{
+ ssize_t err = 0;
+ int read_count;
+ int64_t xattr_len;
+
+ xattr_len = vs->fidp->fs.xattr.len;
+ read_count = xattr_len - vs->off;
+ if (read_count > vs->count) {
+ read_count = vs->count;
+ } else if (read_count < 0) {
+ /*
+ * read beyond XATTR value
+ */
+ read_count = 0;
+ }
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", read_count);
+ vs->offset += pdu_pack(vs->pdu, vs->offset,
+ ((char *)vs->fidp->fs.xattr.value) + vs->off,
+ read_count);
+ err = vs->offset;
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
static void v9fs_read(V9fsState *s, V9fsPDU *pdu)
{
int32_t fid;
@@ -1551,20 +2042,23 @@ static void v9fs_read(V9fsState *s, V9fsPDU *pdu)
goto out;
}
- if (vs->fidp->dir) {
+ if (vs->fidp->fid_type == P9_FID_DIR) {
vs->max_count = vs->count;
vs->count = 0;
if (vs->off == 0) {
- v9fs_do_rewinddir(s, vs->fidp->dir);
+ v9fs_do_rewinddir(s, vs->fidp->fs.dir);
}
v9fs_read_post_rewinddir(s, vs, err);
return;
- } else if (vs->fidp->fd != -1) {
+ } else if (vs->fidp->fid_type == P9_FID_FILE) {
vs->sg = vs->iov;
pdu_marshal(vs->pdu, vs->offset + 4, "v", vs->sg, &vs->cnt);
- err = v9fs_do_lseek(s, vs->fidp->fd, vs->off, SEEK_SET);
+ err = v9fs_do_lseek(s, vs->fidp->fs.fd, vs->off, SEEK_SET);
v9fs_read_post_lseek(s, vs, err);
return;
+ } else if (vs->fidp->fid_type == P9_FID_XATTR) {
+ v9fs_xattr_read(s, vs);
+ return;
} else {
err = -EINVAL;
}
@@ -1573,6 +2067,127 @@ out:
qemu_free(vs);
}
+typedef struct V9fsReadDirState {
+ V9fsPDU *pdu;
+ V9fsFidState *fidp;
+ V9fsQID qid;
+ off_t saved_dir_pos;
+ struct dirent *dent;
+ int32_t count;
+ int32_t max_count;
+ size_t offset;
+ int64_t initial_offset;
+ V9fsString name;
+} V9fsReadDirState;
+
+static void v9fs_readdir_post_seekdir(V9fsState *s, V9fsReadDirState *vs)
+{
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
+ vs->offset += vs->count;
+ complete_pdu(s, vs->pdu, vs->offset);
+ qemu_free(vs);
+ return;
+}
+
+/* Size of each dirent on the wire: size of qid (13) + size of offset (8)
+ * size of type (1) + size of name.size (2) + strlen(name.data)
+ */
+#define V9_READDIR_DATA_SZ (24 + strlen(vs->name.data))
+
+static void v9fs_readdir_post_readdir(V9fsState *s, V9fsReadDirState *vs)
+{
+ int len;
+ size_t size;
+
+ if (vs->dent) {
+ v9fs_string_init(&vs->name);
+ v9fs_string_sprintf(&vs->name, "%s", vs->dent->d_name);
+
+ if ((vs->count + V9_READDIR_DATA_SZ) > vs->max_count) {
+ /* Ran out of buffer. Set dir back to old position and return */
+ v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->saved_dir_pos);
+ v9fs_readdir_post_seekdir(s, vs);
+ return;
+ }
+
+ /* Fill up just the path field of qid because the client uses
+ * only that. To fill the entire qid structure we will have
+ * to stat each dirent found, which is expensive
+ */
+ size = MIN(sizeof(vs->dent->d_ino), sizeof(vs->qid.path));
+ memcpy(&vs->qid.path, &vs->dent->d_ino, size);
+ /* Fill the other fields with dummy values */
+ vs->qid.type = 0;
+ vs->qid.version = 0;
+
+ len = pdu_marshal(vs->pdu, vs->offset+4+vs->count, "Qqbs",
+ &vs->qid, vs->dent->d_off,
+ vs->dent->d_type, &vs->name);
+ vs->count += len;
+ v9fs_string_free(&vs->name);
+ vs->saved_dir_pos = vs->dent->d_off;
+ vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
+ v9fs_readdir_post_readdir(s, vs);
+ return;
+ }
+
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
+ vs->offset += vs->count;
+ complete_pdu(s, vs->pdu, vs->offset);
+ qemu_free(vs);
+ return;
+}
+
+static void v9fs_readdir_post_telldir(V9fsState *s, V9fsReadDirState *vs)
+{
+ vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
+ v9fs_readdir_post_readdir(s, vs);
+ return;
+}
+
+static void v9fs_readdir_post_setdir(V9fsState *s, V9fsReadDirState *vs)
+{
+ vs->saved_dir_pos = v9fs_do_telldir(s, vs->fidp->fs.dir);
+ v9fs_readdir_post_telldir(s, vs);
+ return;
+}
+
+static void v9fs_readdir(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsReadDirState *vs;
+ ssize_t err = 0;
+ size_t offset = 7;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+ vs->count = 0;
+
+ pdu_unmarshal(vs->pdu, offset, "dqd", &fid, &vs->initial_offset,
+ &vs->max_count);
+
+ vs->fidp = lookup_fid(s, fid);
+ if (vs->fidp == NULL || !(vs->fidp->fs.dir)) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (vs->initial_offset == 0) {
+ v9fs_do_rewinddir(s, vs->fidp->fs.dir);
+ } else {
+ v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->initial_offset);
+ }
+
+ v9fs_readdir_post_setdir(s, vs);
+ return;
+
+out:
+ complete_pdu(s, pdu, err);
+ qemu_free(vs);
+ return;
+}
+
static void v9fs_write_post_writev(V9fsState *s, V9fsWriteState *vs,
ssize_t err)
{
@@ -1588,7 +2203,7 @@ static void v9fs_write_post_writev(V9fsState *s, V9fsWriteState *vs,
if (0) {
print_sg(vs->sg, vs->cnt);
}
- vs->len = v9fs_do_writev(s, vs->fidp->fd, vs->sg, vs->cnt);
+ vs->len = v9fs_do_writev(s, vs->fidp->fs.fd, vs->sg, vs->cnt);
} while (vs->len == -1 && errno == EINTR);
if (vs->len == -1) {
err = -errno;
@@ -1617,7 +2232,7 @@ static void v9fs_write_post_lseek(V9fsState *s, V9fsWriteState *vs, ssize_t err)
if (0) {
print_sg(vs->sg, vs->cnt);
}
- vs->len = v9fs_do_writev(s, vs->fidp->fd, vs->sg, vs->cnt);
+ vs->len = v9fs_do_writev(s, vs->fidp->fs.fd, vs->sg, vs->cnt);
} while (vs->len == -1 && errno == EINTR);
if (vs->len == -1) {
err = -errno;
@@ -1631,6 +2246,48 @@ out:
qemu_free(vs);
}
+static void v9fs_xattr_write(V9fsState *s, V9fsWriteState *vs)
+{
+ int i, to_copy;
+ ssize_t err = 0;
+ int write_count;
+ int64_t xattr_len;
+
+ xattr_len = vs->fidp->fs.xattr.len;
+ write_count = xattr_len - vs->off;
+ if (write_count > vs->count) {
+ write_count = vs->count;
+ } else if (write_count < 0) {
+ /*
+ * write beyond XATTR value len specified in
+ * xattrcreate
+ */
+ err = -ENOSPC;
+ goto out;
+ }
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", write_count);
+ err = vs->offset;
+ vs->fidp->fs.xattr.copied_len += write_count;
+ /*
+ * Now copy the content from sg list
+ */
+ for (i = 0; i < vs->cnt; i++) {
+ if (write_count > vs->sg[i].iov_len) {
+ to_copy = vs->sg[i].iov_len;
+ } else {
+ to_copy = write_count;
+ }
+ memcpy((char *)vs->fidp->fs.xattr.value + vs->off,
+ vs->sg[i].iov_base, to_copy);
+ /* updating vs->off since we are not using below */
+ vs->off += to_copy;
+ write_count -= to_copy;
+ }
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
static void v9fs_write(V9fsState *s, V9fsPDU *pdu)
{
int32_t fid;
@@ -1646,7 +2303,7 @@ static void v9fs_write(V9fsState *s, V9fsPDU *pdu)
vs->len = 0;
pdu_unmarshal(vs->pdu, vs->offset, "dqdv", &fid, &vs->off, &vs->count,
- vs->sg, &vs->cnt);
+ vs->sg, &vs->cnt);
vs->fidp = lookup_fid(s, fid);
if (vs->fidp == NULL) {
@@ -1654,12 +2311,22 @@ static void v9fs_write(V9fsState *s, V9fsPDU *pdu)
goto out;
}
- if (vs->fidp->fd == -1) {
+ if (vs->fidp->fid_type == P9_FID_FILE) {
+ if (vs->fidp->fs.fd == -1) {
+ err = -EINVAL;
+ goto out;
+ }
+ } else if (vs->fidp->fid_type == P9_FID_XATTR) {
+ /*
+ * setxattr operation
+ */
+ v9fs_xattr_write(s, vs);
+ return;
+ } else {
err = -EINVAL;
goto out;
}
-
- err = v9fs_do_lseek(s, vs->fidp->fd, vs->off, SEEK_SET);
+ err = v9fs_do_lseek(s, vs->fidp->fs.fd, vs->off, SEEK_SET);
v9fs_write_post_lseek(s, vs, err);
return;
@@ -1669,15 +2336,28 @@ out:
qemu_free(vs);
}
-static void v9fs_post_create(V9fsState *s, V9fsCreateState *vs, int err)
+static void v9fs_create_post_getiounit(V9fsState *s, V9fsCreateState *vs)
{
- if (err == 0) {
- v9fs_string_copy(&vs->fidp->path, &vs->fullname);
- stat_to_qid(&vs->stbuf, &vs->qid);
+ int err;
+ v9fs_string_copy(&vs->fidp->path, &vs->fullname);
+ stat_to_qid(&vs->stbuf, &vs->qid);
- vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, 0);
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, vs->iounit);
+ err = vs->offset;
- err = vs->offset;
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ v9fs_string_free(&vs->extension);
+ v9fs_string_free(&vs->fullname);
+ qemu_free(vs);
+}
+
+static void v9fs_post_create(V9fsState *s, V9fsCreateState *vs, int err)
+{
+ if (err == 0) {
+ vs->iounit = get_iounit(s, &vs->fidp->path);
+ v9fs_create_post_getiounit(s, vs);
+ return;
}
complete_pdu(s, vs->pdu, err);
@@ -1698,9 +2378,10 @@ static void v9fs_create_post_perms(V9fsState *s, V9fsCreateState *vs, int err)
static void v9fs_create_post_opendir(V9fsState *s, V9fsCreateState *vs,
int err)
{
- if (!vs->fidp->dir) {
+ if (!vs->fidp->fs.dir) {
err = -errno;
}
+ vs->fidp->fid_type = P9_FID_DIR;
v9fs_post_create(s, vs, err);
}
@@ -1712,7 +2393,7 @@ static void v9fs_create_post_dir_lstat(V9fsState *s, V9fsCreateState *vs,
goto out;
}
- vs->fidp->dir = v9fs_do_opendir(s, &vs->fullname);
+ vs->fidp->fs.dir = v9fs_do_opendir(s, &vs->fullname);
v9fs_create_post_opendir(s, vs, err);
return;
@@ -1738,22 +2419,22 @@ out:
static void v9fs_create_post_fstat(V9fsState *s, V9fsCreateState *vs, int err)
{
if (err) {
- vs->fidp->fd = -1;
+ vs->fidp->fid_type = P9_FID_NONE;
+ close(vs->fidp->fs.fd);
err = -errno;
}
-
v9fs_post_create(s, vs, err);
return;
}
static void v9fs_create_post_open2(V9fsState *s, V9fsCreateState *vs, int err)
{
- if (vs->fidp->fd == -1) {
+ if (vs->fidp->fs.fd == -1) {
err = -errno;
goto out;
}
-
- err = v9fs_do_fstat(s, vs->fidp->fd, &vs->stbuf);
+ vs->fidp->fid_type = P9_FID_FILE;
+ err = v9fs_do_fstat(s, vs->fidp->fs.fd, &vs->stbuf);
v9fs_create_post_fstat(s, vs, err);
return;
@@ -1772,10 +2453,12 @@ static void v9fs_create_post_lstat(V9fsState *s, V9fsCreateState *vs, int err)
}
if (vs->perm & P9_STAT_MODE_DIR) {
- err = v9fs_do_mkdir(s, vs);
+ err = v9fs_do_mkdir(s, vs->fullname.data, vs->perm & 0777,
+ vs->fidp->uid, -1);
v9fs_create_post_mkdir(s, vs, err);
} else if (vs->perm & P9_STAT_MODE_SYMLINK) {
- err = v9fs_do_symlink(s, vs);
+ err = v9fs_do_symlink(s, vs->fidp, vs->extension.data,
+ vs->fullname.data, -1);
v9fs_create_post_perms(s, vs, err);
} else if (vs->perm & P9_STAT_MODE_LINK) {
int32_t nfid = atoi(vs->extension.data);
@@ -1810,16 +2493,21 @@ static void v9fs_create_post_lstat(V9fsState *s, V9fsCreateState *vs, int err)
}
nmode |= vs->perm & 0777;
- err = v9fs_do_mknod(s, vs, nmode, makedev(major, minor));
+ err = v9fs_do_mknod(s, vs->fullname.data, nmode,
+ makedev(major, minor), vs->fidp->uid, -1);
v9fs_create_post_perms(s, vs, err);
} else if (vs->perm & P9_STAT_MODE_NAMED_PIPE) {
- err = v9fs_do_mknod(s, vs, S_IFIFO | (vs->perm & 0777), 0);
+ err = v9fs_do_mknod(s, vs->fullname.data, S_IFIFO | (vs->perm & 0777),
+ 0, vs->fidp->uid, -1);
v9fs_post_create(s, vs, err);
} else if (vs->perm & P9_STAT_MODE_SOCKET) {
- err = v9fs_do_mknod(s, vs, S_IFSOCK | (vs->perm & 0777), 0);
+ err = v9fs_do_mknod(s, vs->fullname.data, S_IFSOCK | (vs->perm & 0777),
+ 0, vs->fidp->uid, -1);
v9fs_post_create(s, vs, err);
} else {
- vs->fidp->fd = v9fs_do_open2(s, vs);
+ vs->fidp->fs.fd = v9fs_do_open2(s, vs->fullname.data, vs->fidp->uid,
+ -1, omode_to_uflags(vs->mode)|O_CREAT, vs->perm);
+
v9fs_create_post_open2(s, vs, err);
}
@@ -1864,23 +2552,124 @@ out:
qemu_free(vs);
}
+static void v9fs_post_symlink(V9fsState *s, V9fsSymlinkState *vs, int err)
+{
+ if (err == 0) {
+ stat_to_qid(&vs->stbuf, &vs->qid);
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qid);
+ err = vs->offset;
+ } else {
+ err = -errno;
+ }
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ v9fs_string_free(&vs->symname);
+ v9fs_string_free(&vs->fullname);
+ qemu_free(vs);
+}
+
+static void v9fs_symlink_post_do_symlink(V9fsState *s, V9fsSymlinkState *vs,
+ int err)
+{
+ if (err) {
+ goto out;
+ }
+ err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
+out:
+ v9fs_post_symlink(s, vs, err);
+}
+
+static void v9fs_symlink(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t dfid;
+ V9fsSymlinkState *vs;
+ int err = 0;
+ gid_t gid;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ v9fs_string_init(&vs->fullname);
+
+ pdu_unmarshal(vs->pdu, vs->offset, "dssd", &dfid, &vs->name,
+ &vs->symname, &gid);
+
+ vs->dfidp = lookup_fid(s, dfid);
+ if (vs->dfidp == NULL) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ v9fs_string_sprintf(&vs->fullname, "%s/%s", vs->dfidp->path.data,
+ vs->name.data);
+ err = v9fs_do_symlink(s, vs->dfidp, vs->symname.data,
+ vs->fullname.data, gid);
+ v9fs_symlink_post_do_symlink(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ v9fs_string_free(&vs->symname);
+ qemu_free(vs);
+}
+
static void v9fs_flush(V9fsState *s, V9fsPDU *pdu)
{
/* A nop call with no return */
complete_pdu(s, pdu, 7);
}
+static void v9fs_link(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t dfid, oldfid;
+ V9fsFidState *dfidp, *oldfidp;
+ V9fsString name, fullname;
+ size_t offset = 7;
+ int err = 0;
+
+ v9fs_string_init(&fullname);
+
+ pdu_unmarshal(pdu, offset, "dds", &dfid, &oldfid, &name);
+
+ dfidp = lookup_fid(s, dfid);
+ if (dfidp == NULL) {
+ err = -errno;
+ goto out;
+ }
+
+ oldfidp = lookup_fid(s, oldfid);
+ if (oldfidp == NULL) {
+ err = -errno;
+ goto out;
+ }
+
+ v9fs_string_sprintf(&fullname, "%s/%s", dfidp->path.data, name.data);
+ err = offset;
+ err = v9fs_do_link(s, &oldfidp->path, &fullname);
+ if (err) {
+ err = -errno;
+ }
+ v9fs_string_free(&fullname);
+
+out:
+ v9fs_string_free(&name);
+ complete_pdu(s, pdu, err);
+}
+
static void v9fs_remove_post_remove(V9fsState *s, V9fsRemoveState *vs,
int err)
{
- /* For TREMOVE we need to clunk the fid even on failed remove */
- err = free_fid(s, vs->fidp->fid);
if (err < 0) {
- goto out;
+ err = -errno;
+ } else {
+ err = vs->offset;
}
- err = vs->offset;
-out:
+ /* For TREMOVE we need to clunk the fid even on failed remove */
+ free_fid(s, vs->fidp->fid);
+
complete_pdu(s, vs->pdu, err);
qemu_free(vs);
}
@@ -1931,11 +2720,6 @@ static void v9fs_wstat_post_rename(V9fsState *s, V9fsWstatState *vs, int err)
if (err < 0) {
goto out;
}
-
- if (vs->v9stat.name.size != 0) {
- v9fs_string_free(&vs->nname);
- }
-
if (vs->v9stat.length != -1) {
if (v9fs_do_truncate(s, &vs->fidp->path, vs->v9stat.length) < 0) {
err = -errno;
@@ -1950,17 +2734,29 @@ out:
qemu_free(vs);
}
-static void v9fs_wstat_post_chown(V9fsState *s, V9fsWstatState *vs, int err)
+static int v9fs_complete_rename(V9fsState *s, V9fsRenameState *vs)
{
- V9fsFidState *fidp;
- if (err < 0) {
- goto out;
- }
+ int err = 0;
+ char *old_name, *new_name;
+ char *end;
- if (vs->v9stat.name.size != 0) {
- char *old_name, *new_name;
- char *end;
+ if (vs->newdirfid != -1) {
+ V9fsFidState *dirfidp;
+ dirfidp = lookup_fid(s, vs->newdirfid);
+
+ if (dirfidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ BUG_ON(dirfidp->fid_type != P9_FID_NONE);
+ new_name = qemu_mallocz(dirfidp->path.size + vs->name.size + 2);
+
+ strcpy(new_name, dirfidp->path.data);
+ strcat(new_name, "/");
+ strcat(new_name + dirfidp->path.size, vs->name.data);
+ } else {
old_name = vs->fidp->path.data;
end = strrchr(old_name, '/');
if (end) {
@@ -1968,43 +2764,74 @@ static void v9fs_wstat_post_chown(V9fsState *s, V9fsWstatState *vs, int err)
} else {
end = old_name;
}
+ new_name = qemu_mallocz(end - old_name + vs->name.size + 1);
- new_name = qemu_mallocz(end - old_name + vs->v9stat.name.size + 1);
+ strncat(new_name, old_name, end - old_name);
+ strncat(new_name + (end - old_name), vs->name.data, vs->name.size);
+ }
- memcpy(new_name, old_name, end - old_name);
- memcpy(new_name + (end - old_name), vs->v9stat.name.data,
- vs->v9stat.name.size);
- vs->nname.data = new_name;
- vs->nname.size = strlen(new_name);
+ v9fs_string_free(&vs->name);
+ vs->name.data = qemu_strdup(new_name);
+ vs->name.size = strlen(new_name);
- if (strcmp(new_name, vs->fidp->path.data) != 0) {
- if (v9fs_do_rename(s, &vs->fidp->path, &vs->nname)) {
- err = -errno;
- } else {
- /*
- * Fixup fid's pointing to the old name to
- * start pointing to the new name
- */
- for (fidp = s->fid_list; fidp; fidp = fidp->next) {
-
- if (vs->fidp == fidp) {
- /*
- * we replace name of this fid towards the end
- * so that our below strcmp will work
- */
- continue;
- }
- if (!strncmp(vs->fidp->path.data, fidp->path.data,
- strlen(vs->fidp->path.data))) {
- /* replace the name */
- v9fs_fix_path(&fidp->path, &vs->nname,
- strlen(vs->fidp->path.data));
- }
+ if (strcmp(new_name, vs->fidp->path.data) != 0) {
+ if (v9fs_do_rename(s, &vs->fidp->path, &vs->name)) {
+ err = -errno;
+ } else {
+ V9fsFidState *fidp;
+ /*
+ * Fixup fid's pointing to the old name to
+ * start pointing to the new name
+ */
+ for (fidp = s->fid_list; fidp; fidp = fidp->next) {
+ if (vs->fidp == fidp) {
+ /*
+ * we replace name of this fid towards the end
+ * so that our below strcmp will work
+ */
+ continue;
+ }
+ if (!strncmp(vs->fidp->path.data, fidp->path.data,
+ strlen(vs->fidp->path.data))) {
+ /* replace the name */
+ v9fs_fix_path(&fidp->path, &vs->name,
+ strlen(vs->fidp->path.data));
}
- v9fs_string_copy(&vs->fidp->path, &vs->nname);
}
+ v9fs_string_copy(&vs->fidp->path, &vs->name);
}
}
+out:
+ v9fs_string_free(&vs->name);
+ return err;
+}
+
+static void v9fs_rename_post_rename(V9fsState *s, V9fsRenameState *vs, int err)
+{
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_wstat_post_chown(V9fsState *s, V9fsWstatState *vs, int err)
+{
+ if (err < 0) {
+ goto out;
+ }
+
+ if (vs->v9stat.name.size != 0) {
+ V9fsRenameState *vr;
+
+ vr = qemu_mallocz(sizeof(V9fsRenameState));
+ vr->newdirfid = -1;
+ vr->pdu = vs->pdu;
+ vr->fidp = vs->fidp;
+ vr->offset = vs->offset;
+ vr->name.size = vs->v9stat.name.size;
+ vr->name.data = qemu_strdup(vs->v9stat.name.data);
+
+ err = v9fs_complete_rename(s, vr);
+ qemu_free(vr);
+ }
v9fs_wstat_post_rename(s, vs, err);
return;
@@ -2014,6 +2841,34 @@ out:
qemu_free(vs);
}
+static void v9fs_rename(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsRenameState *vs;
+ ssize_t err = 0;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ pdu_unmarshal(vs->pdu, vs->offset, "dds", &fid, &vs->newdirfid, &vs->name);
+
+ vs->fidp = lookup_fid(s, fid);
+ if (vs->fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ BUG_ON(vs->fidp->fid_type != P9_FID_NONE);
+
+ err = v9fs_complete_rename(s, vs);
+ v9fs_rename_post_rename(s, vs, err);
+ return;
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
static void v9fs_wstat_post_utime(V9fsState *s, V9fsWstatState *vs, int err)
{
if (err < 0) {
@@ -2041,11 +2896,22 @@ static void v9fs_wstat_post_chmod(V9fsState *s, V9fsWstatState *vs, int err)
goto out;
}
- if (vs->v9stat.mtime != -1) {
- struct utimbuf tb;
- tb.actime = 0;
- tb.modtime = vs->v9stat.mtime;
- if (v9fs_do_utime(s, &vs->fidp->path, &tb)) {
+ if (vs->v9stat.mtime != -1 || vs->v9stat.atime != -1) {
+ struct timespec times[2];
+ if (vs->v9stat.atime != -1) {
+ times[0].tv_sec = vs->v9stat.atime;
+ times[0].tv_nsec = 0;
+ } else {
+ times[0].tv_nsec = UTIME_OMIT;
+ }
+ if (vs->v9stat.mtime != -1) {
+ times[1].tv_sec = vs->v9stat.mtime;
+ times[1].tv_nsec = 0;
+ } else {
+ times[1].tv_nsec = UTIME_OMIT;
+ }
+
+ if (v9fs_do_utimensat(s, &vs->fidp->path, times)) {
err = -errno;
}
}
@@ -2120,7 +2986,7 @@ static void v9fs_wstat(V9fsState *s, V9fsPDU *pdu)
/* do we need to sync the file? */
if (donttouch_stat(&vs->v9stat)) {
- err = v9fs_do_fsync(s, vs->fidp->fd);
+ err = v9fs_do_fsync(s, vs->fidp->fs.fd);
v9fs_wstat_post_fsync(s, vs, err);
return;
}
@@ -2140,10 +3006,415 @@ out:
qemu_free(vs);
}
+static void v9fs_statfs_post_statfs(V9fsState *s, V9fsStatfsState *vs, int err)
+{
+ int32_t bsize_factor;
+
+ if (err) {
+ err = -errno;
+ goto out;
+ }
+
+ /*
+ * compute bsize factor based on host file system block size
+ * and client msize
+ */
+ bsize_factor = (s->msize - P9_IOHDRSZ)/vs->stbuf.f_bsize;
+ if (!bsize_factor) {
+ bsize_factor = 1;
+ }
+ vs->v9statfs.f_type = vs->stbuf.f_type;
+ vs->v9statfs.f_bsize = vs->stbuf.f_bsize;
+ vs->v9statfs.f_bsize *= bsize_factor;
+ /*
+ * f_bsize is adjusted(multiplied) by bsize factor, so we need to
+ * adjust(divide) the number of blocks, free blocks and available
+ * blocks by bsize factor
+ */
+ vs->v9statfs.f_blocks = vs->stbuf.f_blocks/bsize_factor;
+ vs->v9statfs.f_bfree = vs->stbuf.f_bfree/bsize_factor;
+ vs->v9statfs.f_bavail = vs->stbuf.f_bavail/bsize_factor;
+ vs->v9statfs.f_files = vs->stbuf.f_files;
+ vs->v9statfs.f_ffree = vs->stbuf.f_ffree;
+ vs->v9statfs.fsid_val = (unsigned int) vs->stbuf.f_fsid.__val[0] |
+ (unsigned long long)vs->stbuf.f_fsid.__val[1] << 32;
+ vs->v9statfs.f_namelen = vs->stbuf.f_namelen;
+
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "ddqqqqqqd",
+ vs->v9statfs.f_type, vs->v9statfs.f_bsize, vs->v9statfs.f_blocks,
+ vs->v9statfs.f_bfree, vs->v9statfs.f_bavail, vs->v9statfs.f_files,
+ vs->v9statfs.f_ffree, vs->v9statfs.fsid_val,
+ vs->v9statfs.f_namelen);
+
+out:
+ complete_pdu(s, vs->pdu, vs->offset);
+ qemu_free(vs);
+}
+
+static void v9fs_statfs(V9fsState *s, V9fsPDU *pdu)
+{
+ V9fsStatfsState *vs;
+ ssize_t err = 0;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ memset(&vs->v9statfs, 0, sizeof(vs->v9statfs));
+
+ pdu_unmarshal(vs->pdu, vs->offset, "d", &vs->fid);
+
+ vs->fidp = lookup_fid(s, vs->fid);
+ if (vs->fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ err = v9fs_do_statfs(s, &vs->fidp->path, &vs->stbuf);
+ v9fs_statfs_post_statfs(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_mknod_post_lstat(V9fsState *s, V9fsMkState *vs, int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ stat_to_qid(&vs->stbuf, &vs->qid);
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qid);
+ err = vs->offset;
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->fullname);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_mknod_post_mknod(V9fsState *s, V9fsMkState *vs, int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
+ v9fs_mknod_post_lstat(s, vs, err);
+ return;
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->fullname);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_mknod(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsMkState *vs;
+ int err = 0;
+ V9fsFidState *fidp;
+ gid_t gid;
+ int mode;
+ int major, minor;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ v9fs_string_init(&vs->fullname);
+ pdu_unmarshal(vs->pdu, vs->offset, "dsdddd", &fid, &vs->name, &mode,
+ &major, &minor, &gid);
+
+ fidp = lookup_fid(s, fid);
+ if (fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ v9fs_string_sprintf(&vs->fullname, "%s/%s", fidp->path.data, vs->name.data);
+ err = v9fs_do_mknod(s, vs->fullname.data, mode, makedev(major, minor),
+ fidp->uid, gid);
+ v9fs_mknod_post_mknod(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->fullname);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_mkdir_post_lstat(V9fsState *s, V9fsMkState *vs, int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ stat_to_qid(&vs->stbuf, &vs->qid);
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qid);
+ err = vs->offset;
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->fullname);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_mkdir_post_mkdir(V9fsState *s, V9fsMkState *vs, int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
+ v9fs_mkdir_post_lstat(s, vs, err);
+ return;
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->fullname);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_mkdir(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsMkState *vs;
+ int err = 0;
+ V9fsFidState *fidp;
+ gid_t gid;
+ int mode;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ v9fs_string_init(&vs->fullname);
+ pdu_unmarshal(vs->pdu, vs->offset, "dsdd", &fid, &vs->name, &mode,
+ &gid);
+
+ fidp = lookup_fid(s, fid);
+ if (fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ v9fs_string_sprintf(&vs->fullname, "%s/%s", fidp->path.data, vs->name.data);
+ err = v9fs_do_mkdir(s, vs->fullname.data, mode, fidp->uid, gid);
+ v9fs_mkdir_post_mkdir(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->fullname);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_post_xattr_getvalue(V9fsState *s, V9fsXattrState *vs, int err)
+{
+
+ if (err < 0) {
+ err = -errno;
+ free_fid(s, vs->xattr_fidp->fid);
+ goto out;
+ }
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "q", vs->size);
+ err = vs->offset;
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+ return;
+}
+
+static void v9fs_post_xattr_check(V9fsState *s, V9fsXattrState *vs, ssize_t err)
+{
+ if (err < 0) {
+ err = -errno;
+ free_fid(s, vs->xattr_fidp->fid);
+ goto out;
+ }
+ /*
+ * Read the xattr value
+ */
+ vs->xattr_fidp->fs.xattr.len = vs->size;
+ vs->xattr_fidp->fid_type = P9_FID_XATTR;
+ vs->xattr_fidp->fs.xattr.copied_len = -1;
+ if (vs->size) {
+ vs->xattr_fidp->fs.xattr.value = qemu_malloc(vs->size);
+ err = v9fs_do_lgetxattr(s, &vs->xattr_fidp->path,
+ &vs->name, vs->xattr_fidp->fs.xattr.value,
+ vs->xattr_fidp->fs.xattr.len);
+ }
+ v9fs_post_xattr_getvalue(s, vs, err);
+ return;
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_post_lxattr_getvalue(V9fsState *s,
+ V9fsXattrState *vs, int err)
+{
+ if (err < 0) {
+ err = -errno;
+ free_fid(s, vs->xattr_fidp->fid);
+ goto out;
+ }
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "q", vs->size);
+ err = vs->offset;
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+ return;
+}
+
+static void v9fs_post_lxattr_check(V9fsState *s,
+ V9fsXattrState *vs, ssize_t err)
+{
+ if (err < 0) {
+ err = -errno;
+ free_fid(s, vs->xattr_fidp->fid);
+ goto out;
+ }
+ /*
+ * Read the xattr value
+ */
+ vs->xattr_fidp->fs.xattr.len = vs->size;
+ vs->xattr_fidp->fid_type = P9_FID_XATTR;
+ vs->xattr_fidp->fs.xattr.copied_len = -1;
+ if (vs->size) {
+ vs->xattr_fidp->fs.xattr.value = qemu_malloc(vs->size);
+ err = v9fs_do_llistxattr(s, &vs->xattr_fidp->path,
+ vs->xattr_fidp->fs.xattr.value,
+ vs->xattr_fidp->fs.xattr.len);
+ }
+ v9fs_post_lxattr_getvalue(s, vs, err);
+ return;
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_xattrwalk(V9fsState *s, V9fsPDU *pdu)
+{
+ ssize_t err = 0;
+ V9fsXattrState *vs;
+ int32_t fid, newfid;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ pdu_unmarshal(vs->pdu, vs->offset, "dds", &fid, &newfid, &vs->name);
+ vs->file_fidp = lookup_fid(s, fid);
+ if (vs->file_fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ vs->xattr_fidp = alloc_fid(s, newfid);
+ if (vs->xattr_fidp == NULL) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ v9fs_string_copy(&vs->xattr_fidp->path, &vs->file_fidp->path);
+ if (vs->name.data[0] == 0) {
+ /*
+ * listxattr request. Get the size first
+ */
+ vs->size = v9fs_do_llistxattr(s, &vs->xattr_fidp->path,
+ NULL, 0);
+ if (vs->size < 0) {
+ err = vs->size;
+ }
+ v9fs_post_lxattr_check(s, vs, err);
+ return;
+ } else {
+ /*
+ * specific xattr fid. We check for xattr
+ * presence also collect the xattr size
+ */
+ vs->size = v9fs_do_lgetxattr(s, &vs->xattr_fidp->path,
+ &vs->name, NULL, 0);
+ if (vs->size < 0) {
+ err = vs->size;
+ }
+ v9fs_post_xattr_check(s, vs, err);
+ return;
+ }
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_xattrcreate(V9fsState *s, V9fsPDU *pdu)
+{
+ int flags;
+ int32_t fid;
+ ssize_t err = 0;
+ V9fsXattrState *vs;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ pdu_unmarshal(vs->pdu, vs->offset, "dsqd",
+ &fid, &vs->name, &vs->size, &flags);
+
+ vs->file_fidp = lookup_fid(s, fid);
+ if (vs->file_fidp == NULL) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ /* Make the file fid point to xattr */
+ vs->xattr_fidp = vs->file_fidp;
+ vs->xattr_fidp->fid_type = P9_FID_XATTR;
+ vs->xattr_fidp->fs.xattr.copied_len = 0;
+ vs->xattr_fidp->fs.xattr.len = vs->size;
+ vs->xattr_fidp->fs.xattr.flags = flags;
+ v9fs_string_init(&vs->xattr_fidp->fs.xattr.name);
+ v9fs_string_copy(&vs->xattr_fidp->fs.xattr.name, &vs->name);
+ if (vs->size)
+ vs->xattr_fidp->fs.xattr.value = qemu_malloc(vs->size);
+ else
+ vs->xattr_fidp->fs.xattr.value = NULL;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
typedef void (pdu_handler_t)(V9fsState *s, V9fsPDU *pdu);
static pdu_handler_t *pdu_handlers[] = {
+ [P9_TREADDIR] = v9fs_readdir,
+ [P9_TSTATFS] = v9fs_statfs,
+ [P9_TGETATTR] = v9fs_getattr,
+ [P9_TSETATTR] = v9fs_setattr,
+ [P9_TXATTRWALK] = v9fs_xattrwalk,
+ [P9_TXATTRCREATE] = v9fs_xattrcreate,
+ [P9_TMKNOD] = v9fs_mknod,
+ [P9_TRENAME] = v9fs_rename,
+ [P9_TMKDIR] = v9fs_mkdir,
[P9_TVERSION] = v9fs_version,
+ [P9_TLOPEN] = v9fs_open,
[P9_TATTACH] = v9fs_attach,
[P9_TSTAT] = v9fs_stat,
[P9_TWALK] = v9fs_walk,
@@ -2154,7 +3425,10 @@ static pdu_handler_t *pdu_handlers[] = {
[P9_TAUTH] = v9fs_auth,
#endif
[P9_TFLUSH] = v9fs_flush,
+ [P9_TLINK] = v9fs_link,
+ [P9_TSYMLINK] = v9fs_symlink,
[P9_TCREATE] = v9fs_create,
+ [P9_TLCREATE] = v9fs_lcreate,
[P9_TWRITE] = v9fs_write,
[P9_TWSTAT] = v9fs_wstat,
[P9_TREMOVE] = v9fs_remove,
@@ -2272,12 +3546,18 @@ VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf)
* Client user credentials are saved in extended attributes.
*/
s->ctx.fs_sm = SM_MAPPED;
+ } else if (!strcmp(fse->security_model, "none")) {
+ /*
+ * Files on the fileserver are set to QEMU credentials.
+ */
+ s->ctx.fs_sm = SM_NONE;
+
} else {
- /* user haven't specified a correct security option */
- fprintf(stderr, "one of the following must be specified as the"
+ fprintf(stderr, "Default to security_model=none. You may want"
+ " enable advanced security model using "
"security option:\n\t security_model=passthrough \n\t "
"security_model=mapped\n");
- return NULL;
+ s->ctx.fs_sm = SM_NONE;
}
if (lstat(fse->path, &stat)) {
diff --git a/hw/virtio-9p.h b/hw/virtio-9p.h
index 67f808761f..0816ad66ec 100644
--- a/hw/virtio-9p.h
+++ b/hw/virtio-9p.h
@@ -13,6 +13,34 @@
#define VIRTIO_9P_MOUNT_TAG 0
enum {
+ P9_TLERROR = 6,
+ P9_RLERROR,
+ P9_TSTATFS = 8,
+ P9_RSTATFS,
+ P9_TLOPEN = 12,
+ P9_RLOPEN,
+ P9_TLCREATE = 14,
+ P9_RLCREATE,
+ P9_TSYMLINK = 16,
+ P9_RSYMLINK,
+ P9_TMKNOD = 18,
+ P9_RMKNOD,
+ P9_TRENAME = 20,
+ P9_RRENAME,
+ P9_TGETATTR = 24,
+ P9_RGETATTR,
+ P9_TSETATTR = 26,
+ P9_RSETATTR,
+ P9_TXATTRWALK = 30,
+ P9_RXATTRWALK,
+ P9_TXATTRCREATE = 32,
+ P9_RXATTRCREATE,
+ P9_TREADDIR = 40,
+ P9_RREADDIR,
+ P9_TLINK = 70,
+ P9_RLINK,
+ P9_TMKDIR = 72,
+ P9_RMKDIR,
P9_TVERSION = 100,
P9_RVERSION,
P9_TAUTH = 102,
@@ -57,10 +85,21 @@ enum {
P9_QTFILE = 0x00,
};
+enum p9_proto_version {
+ V9FS_PROTO_2000U = 0x01,
+ V9FS_PROTO_2000L = 0x02,
+};
+
#define P9_NOTAG (u16)(~0)
#define P9_NOFID (u32)(~0)
#define P9_MAXWELEM 16
+/*
+ * ample room for Twrite/Rread header
+ * size[4] Tread/Twrite tag[2] fid[4] offset[8] count[4]
+ */
+#define P9_IOHDRSZ 24
+
typedef struct V9fsPDU V9fsPDU;
struct V9fsPDU
@@ -122,12 +161,32 @@ typedef struct V9fsStat
int32_t n_muid;
} V9fsStat;
+enum {
+ P9_FID_NONE = 0,
+ P9_FID_FILE,
+ P9_FID_DIR,
+ P9_FID_XATTR,
+};
+
+typedef struct V9fsXattr
+{
+ int64_t copied_len;
+ int64_t len;
+ void *value;
+ V9fsString name;
+ int flags;
+} V9fsXattr;
+
struct V9fsFidState
{
+ int fid_type;
int32_t fid;
V9fsString path;
- int fd;
- DIR *dir;
+ union {
+ int fd;
+ DIR *dir;
+ V9fsXattr xattr;
+ } fs;
uid_t uid;
V9fsFidState *next;
};
@@ -144,6 +203,8 @@ typedef struct V9fsState
uint16_t tag_len;
uint8_t *tag;
size_t config_size;
+ enum p9_proto_version proto_version;
+ int32_t msize;
} V9fsState;
typedef struct V9fsCreateState {
@@ -157,8 +218,20 @@ typedef struct V9fsCreateState {
V9fsString name;
V9fsString extension;
V9fsString fullname;
+ int iounit;
} V9fsCreateState;
+typedef struct V9fsLcreateState {
+ V9fsPDU *pdu;
+ size_t offset;
+ V9fsFidState *fidp;
+ V9fsQID qid;
+ int32_t iounit;
+ struct stat stbuf;
+ V9fsString name;
+ V9fsString fullname;
+} V9fsLcreateState;
+
typedef struct V9fsStatState {
V9fsPDU *pdu;
size_t offset;
@@ -167,6 +240,37 @@ typedef struct V9fsStatState {
struct stat stbuf;
} V9fsStatState;
+typedef struct V9fsStatDotl {
+ uint64_t st_result_mask;
+ V9fsQID qid;
+ uint32_t st_mode;
+ uint32_t st_uid;
+ uint32_t st_gid;
+ uint64_t st_nlink;
+ uint64_t st_rdev;
+ uint64_t st_size;
+ uint64_t st_blksize;
+ uint64_t st_blocks;
+ uint64_t st_atime_sec;
+ uint64_t st_atime_nsec;
+ uint64_t st_mtime_sec;
+ uint64_t st_mtime_nsec;
+ uint64_t st_ctime_sec;
+ uint64_t st_ctime_nsec;
+ uint64_t st_btime_sec;
+ uint64_t st_btime_nsec;
+ uint64_t st_gen;
+ uint64_t st_data_version;
+} V9fsStatDotl;
+
+typedef struct V9fsStatStateDotl {
+ V9fsPDU *pdu;
+ size_t offset;
+ V9fsStatDotl v9stat_dotl;
+ struct stat stbuf;
+} V9fsStatStateDotl;
+
+
typedef struct V9fsWalkState {
V9fsPDU *pdu;
size_t offset;
@@ -183,10 +287,11 @@ typedef struct V9fsWalkState {
typedef struct V9fsOpenState {
V9fsPDU *pdu;
size_t offset;
- int8_t mode;
+ int32_t mode;
V9fsFidState *fidp;
V9fsQID qid;
struct stat stbuf;
+ int iounit;
} V9fsOpenState;
typedef struct V9fsReadState {
@@ -235,9 +340,41 @@ typedef struct V9fsWstatState
V9fsStat v9stat;
V9fsFidState *fidp;
struct stat stbuf;
- V9fsString nname;
} V9fsWstatState;
+typedef struct V9fsSymlinkState
+{
+ V9fsPDU *pdu;
+ size_t offset;
+ V9fsString name;
+ V9fsString symname;
+ V9fsString fullname;
+ V9fsFidState *dfidp;
+ V9fsQID qid;
+ struct stat stbuf;
+} V9fsSymlinkState;
+
+typedef struct V9fsIattr
+{
+ int32_t valid;
+ int32_t mode;
+ int32_t uid;
+ int32_t gid;
+ int64_t size;
+ int64_t atime_sec;
+ int64_t atime_nsec;
+ int64_t mtime_sec;
+ int64_t mtime_nsec;
+} V9fsIattr;
+
+typedef struct V9fsSetattrState
+{
+ V9fsPDU *pdu;
+ size_t offset;
+ V9fsIattr v9iattr;
+ V9fsFidState *fidp;
+} V9fsSetattrState;
+
struct virtio_9p_config
{
/* number of characters in tag */
@@ -246,6 +383,57 @@ struct virtio_9p_config
uint8_t tag[0];
} __attribute__((packed));
+typedef struct V9fsStatfs
+{
+ uint32_t f_type;
+ uint32_t f_bsize;
+ uint64_t f_blocks;
+ uint64_t f_bfree;
+ uint64_t f_bavail;
+ uint64_t f_files;
+ uint64_t f_ffree;
+ uint64_t fsid_val;
+ uint32_t f_namelen;
+} V9fsStatfs;
+
+typedef struct V9fsStatfsState {
+ V9fsPDU *pdu;
+ size_t offset;
+ int32_t fid;
+ V9fsStatfs v9statfs;
+ V9fsFidState *fidp;
+ struct statfs stbuf;
+} V9fsStatfsState;
+
+typedef struct V9fsMkState {
+ V9fsPDU *pdu;
+ size_t offset;
+ V9fsQID qid;
+ struct stat stbuf;
+ V9fsString name;
+ V9fsString fullname;
+} V9fsMkState;
+
+typedef struct V9fsRenameState {
+ V9fsPDU *pdu;
+ V9fsFidState *fidp;
+ size_t offset;
+ int32_t newdirfid;
+ V9fsString name;
+} V9fsRenameState;
+
+typedef struct V9fsXattrState
+{
+ V9fsPDU *pdu;
+ size_t offset;
+ V9fsFidState *file_fidp;
+ V9fsFidState *xattr_fidp;
+ V9fsString name;
+ int64_t size;
+ int flags;
+ void *value;
+} V9fsXattrState;
+
extern size_t pdu_packunpack(void *addr, struct iovec *sg, int sg_count,
size_t offset, size_t size, int pack);
diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c
index c3a73438f9..395eb9a068 100644
--- a/hw/virtio-blk.c
+++ b/hw/virtio-blk.c
@@ -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;
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index 075f72df2d..0a9cae22c4 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -36,7 +36,10 @@ typedef struct VirtIONet
VirtQueue *ctrl_vq;
NICState *nic;
QEMUTimer *tx_timer;
- int tx_timer_active;
+ QEMUBH *tx_bh;
+ uint32_t tx_timeout;
+ int32_t tx_burst;
+ int tx_waiting;
uint32_t has_vnet_hdr;
uint8_t has_ufo;
struct {
@@ -619,7 +622,7 @@ static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_
return size;
}
-static void virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq);
+static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq);
static void virtio_net_tx_complete(VLANClientState *nc, ssize_t len)
{
@@ -635,16 +638,18 @@ static void virtio_net_tx_complete(VLANClientState *nc, ssize_t len)
}
/* TX */
-static void virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq)
+static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq)
{
VirtQueueElement elem;
+ int32_t num_packets = 0;
- if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))
- return;
+ if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) {
+ return num_packets;
+ }
if (n->async_tx.elem.out_num) {
virtio_queue_set_notification(n->tx_vq, 0);
- return;
+ return num_packets;
}
while (virtqueue_pop(vq, &elem)) {
@@ -681,38 +686,55 @@ static void virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq)
virtio_queue_set_notification(n->tx_vq, 0);
n->async_tx.elem = elem;
n->async_tx.len = len;
- return;
+ return -EBUSY;
}
len += ret;
virtqueue_push(vq, &elem, len);
virtio_notify(&n->vdev, vq);
+
+ if (++num_packets >= n->tx_burst) {
+ break;
+ }
}
+ return num_packets;
}
-static void virtio_net_handle_tx(VirtIODevice *vdev, VirtQueue *vq)
+static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq)
{
VirtIONet *n = to_virtio_net(vdev);
- if (n->tx_timer_active) {
+ if (n->tx_waiting) {
virtio_queue_set_notification(vq, 1);
qemu_del_timer(n->tx_timer);
- n->tx_timer_active = 0;
+ n->tx_waiting = 0;
virtio_net_flush_tx(n, vq);
} else {
qemu_mod_timer(n->tx_timer,
- qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL);
- n->tx_timer_active = 1;
+ qemu_get_clock(vm_clock) + n->tx_timeout);
+ n->tx_waiting = 1;
virtio_queue_set_notification(vq, 0);
}
}
+static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIONet *n = to_virtio_net(vdev);
+
+ if (unlikely(n->tx_waiting)) {
+ return;
+ }
+ virtio_queue_set_notification(vq, 0);
+ qemu_bh_schedule(n->tx_bh);
+ n->tx_waiting = 1;
+}
+
static void virtio_net_tx_timer(void *opaque)
{
VirtIONet *n = opaque;
- n->tx_timer_active = 0;
+ n->tx_waiting = 0;
/* Just in case the driver is not ready on more */
if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))
@@ -722,6 +744,41 @@ static void virtio_net_tx_timer(void *opaque)
virtio_net_flush_tx(n, n->tx_vq);
}
+static void virtio_net_tx_bh(void *opaque)
+{
+ VirtIONet *n = opaque;
+ int32_t ret;
+
+ n->tx_waiting = 0;
+
+ /* Just in case the driver is not ready on more */
+ if (unlikely(!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)))
+ return;
+
+ ret = virtio_net_flush_tx(n, n->tx_vq);
+ if (ret == -EBUSY) {
+ return; /* Notification re-enable handled by tx_complete */
+ }
+
+ /* If we flush a full burst of packets, assume there are
+ * more coming and immediately reschedule */
+ if (ret >= n->tx_burst) {
+ qemu_bh_schedule(n->tx_bh);
+ n->tx_waiting = 1;
+ return;
+ }
+
+ /* If less than a full burst, re-enable notification and flush
+ * anything that may have come in while we weren't looking. If
+ * we find something, assume the guest is still active and reschedule */
+ virtio_queue_set_notification(n->tx_vq, 1);
+ if (virtio_net_flush_tx(n, n->tx_vq) > 0) {
+ virtio_queue_set_notification(n->tx_vq, 0);
+ qemu_bh_schedule(n->tx_bh);
+ n->tx_waiting = 1;
+ }
+}
+
static void virtio_net_save(QEMUFile *f, void *opaque)
{
VirtIONet *n = opaque;
@@ -735,7 +792,7 @@ static void virtio_net_save(QEMUFile *f, void *opaque)
virtio_save(&n->vdev, f);
qemu_put_buffer(f, n->mac, ETH_ALEN);
- qemu_put_be32(f, n->tx_timer_active);
+ qemu_put_be32(f, n->tx_waiting);
qemu_put_be32(f, n->mergeable_rx_bufs);
qemu_put_be16(f, n->status);
qemu_put_byte(f, n->promisc);
@@ -764,7 +821,7 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
virtio_load(&n->vdev, f);
qemu_get_buffer(f, n->mac, ETH_ALEN);
- n->tx_timer_active = qemu_get_be32(f);
+ n->tx_waiting = qemu_get_be32(f);
n->mergeable_rx_bufs = qemu_get_be32(f);
if (version_id >= 3)
@@ -840,9 +897,13 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
}
n->mac_table.first_multi = i;
- if (n->tx_timer_active) {
- qemu_mod_timer(n->tx_timer,
- qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL);
+ if (n->tx_waiting) {
+ if (n->tx_timer) {
+ qemu_mod_timer(n->tx_timer,
+ qemu_get_clock(vm_clock) + n->tx_timeout);
+ } else {
+ qemu_bh_schedule(n->tx_bh);
+ }
}
return 0;
}
@@ -903,7 +964,8 @@ static void virtio_net_vmstate_change(void *opaque, int running, int reason)
virtio_net_set_status(&n->vdev, n->vdev.status & status);
}
-VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf)
+VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf,
+ virtio_net_conf *net)
{
VirtIONet *n;
@@ -919,7 +981,22 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf)
n->vdev.reset = virtio_net_reset;
n->vdev.set_status = virtio_net_set_status;
n->rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx);
- n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx);
+
+ if (net->tx && strcmp(net->tx, "timer") && strcmp(net->tx, "bh")) {
+ fprintf(stderr, "virtio-net: "
+ "Unknown option tx=%s, valid options: \"timer\" \"bh\"\n",
+ net->tx);
+ fprintf(stderr, "Defaulting to \"bh\"\n");
+ }
+
+ if (net->tx && !strcmp(net->tx, "timer")) {
+ n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx_timer);
+ n->tx_timer = qemu_new_timer(vm_clock, virtio_net_tx_timer, n);
+ n->tx_timeout = net->txtimer;
+ } else {
+ n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx_bh);
+ n->tx_bh = qemu_bh_new(virtio_net_tx_bh, n);
+ }
n->ctrl_vq = virtio_add_queue(&n->vdev, 64, virtio_net_handle_ctrl);
qemu_macaddr_default_if_unset(&conf->macaddr);
memcpy(&n->mac[0], &conf->macaddr, sizeof(n->mac));
@@ -929,8 +1006,8 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf)
qemu_format_nic_info_str(&n->nic->nc, conf->macaddr.a);
- n->tx_timer = qemu_new_timer(vm_clock, virtio_net_tx_timer, n);
- n->tx_timer_active = 0;
+ n->tx_waiting = 0;
+ n->tx_burst = net->txburst;
n->mergeable_rx_bufs = 0;
n->promisc = 1; /* for compatibility */
@@ -962,8 +1039,12 @@ void virtio_net_exit(VirtIODevice *vdev)
qemu_free(n->mac_table.macs);
qemu_free(n->vlans);
- qemu_del_timer(n->tx_timer);
- qemu_free_timer(n->tx_timer);
+ if (n->tx_timer) {
+ qemu_del_timer(n->tx_timer);
+ qemu_free_timer(n->tx_timer);
+ } else {
+ qemu_bh_delete(n->tx_bh);
+ }
virtio_cleanup(&n->vdev);
qemu_del_vlan_client(&n->nic->nc);
diff --git a/hw/virtio-net.h b/hw/virtio-net.h
index 235f1a9fa8..8af9a1ce55 100644
--- a/hw/virtio-net.h
+++ b/hw/virtio-net.h
@@ -49,6 +49,20 @@
#define TX_TIMER_INTERVAL 150000 /* 150 us */
+/* Limit the number of packets that can be sent via a single flush
+ * of the TX queue. This gives us a guaranteed exit condition and
+ * ensures fairness in the io path. 256 conveniently matches the
+ * length of the TX queue and shows a good balance of performance
+ * and latency. */
+#define TX_BURST 256
+
+typedef struct virtio_net_conf
+{
+ uint32_t txtimer;
+ int32_t txburst;
+ char *tx;
+} virtio_net_conf;
+
/* Maximum packet size we can receive from tap device: header + 64k */
#define VIRTIO_NET_MAX_BUFSIZE (sizeof(struct virtio_net_hdr) + (64 << 10))
diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c
index 6e8f88a141..86e6b0a561 100644
--- a/hw/virtio-pci.c
+++ b/hw/virtio-pci.c
@@ -107,6 +107,7 @@ typedef struct {
#endif
/* Max. number of ports we can have for a the virtio-serial device */
uint32_t max_virtserial_ports;
+ virtio_net_conf net;
} VirtIOPCIProxy;
/* virtio device */
@@ -613,7 +614,7 @@ static int virtio_net_init_pci(PCIDevice *pci_dev)
VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
VirtIODevice *vdev;
- vdev = virtio_net_init(&pci_dev->qdev, &proxy->nic);
+ vdev = virtio_net_init(&pci_dev->qdev, &proxy->nic, &proxy->net);
vdev->nvectors = proxy->nvectors;
virtio_init_pci(proxy, vdev,
@@ -690,6 +691,11 @@ static PCIDeviceInfo virtio_info[] = {
DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3),
DEFINE_VIRTIO_NET_FEATURES(VirtIOPCIProxy, host_features),
DEFINE_NIC_PROPERTIES(VirtIOPCIProxy, nic),
+ DEFINE_PROP_UINT32("x-txtimer", VirtIOPCIProxy,
+ net.txtimer, TX_TIMER_INTERVAL),
+ DEFINE_PROP_INT32("x-txburst", VirtIOPCIProxy,
+ net.txburst, TX_BURST),
+ DEFINE_PROP_STRING("tx", VirtIOPCIProxy, net.tx),
DEFINE_PROP_END_OF_LIST(),
},
.qdev.reset = virtio_pci_reset,
diff --git a/hw/virtio.c b/hw/virtio.c
index 4475bb3e44..85312b3ebe 100644
--- a/hw/virtio.c
+++ b/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,28 +403,19 @@ 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);
- }
/* If we've got too many, that implies a descriptor loop. */
if ((elem->in_num + elem->out_num) > max) {
@@ -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++;
diff --git a/hw/virtio.h b/hw/virtio.h
index 5836ab61e7..96514e6c8b 100644
--- a/hw/virtio.h
+++ b/hw/virtio.h
@@ -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);
@@ -185,7 +188,9 @@ void virtio_bind_device(VirtIODevice *vdev, const VirtIOBindings *binding,
/* Base devices. */
VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf);
-VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf);
+struct virtio_net_conf;
+VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf,
+ struct virtio_net_conf *net);
VirtIODevice *virtio_serial_init(DeviceState *dev, uint32_t max_nr_ports);
VirtIODevice *virtio_balloon_init(DeviceState *dev);
#ifdef CONFIG_LINUX