]> git.proxmox.com Git - qemu.git/blobdiff - nbd.c
vnc: tight: Fix crash after 2GB of output
[qemu.git] / nbd.c
diff --git a/nbd.c b/nbd.c
index a9f295f2bbbc20e58802b1ed0d76420490537114..d8ebc4298fc0f5bd85b7ea1ff7eafd1cdb723af8 100644 (file)
--- a/nbd.c
+++ b/nbd.c
@@ -23,7 +23,7 @@
 #ifndef _WIN32
 #include <sys/ioctl.h>
 #endif
-#ifdef __sun__
+#if defined(__sun__) || defined(__HAIKU__)
 #include <sys/ioccom.h>
 #endif
 #include <ctype.h>
@@ -49,6 +49,7 @@
 
 /* This is all part of the "official" NBD API */
 
+#define NBD_REPLY_SIZE         (4 + 4 + 8)
 #define NBD_REQUEST_MAGIC       0x25609513
 #define NBD_REPLY_MAGIC         0x67446698
 
@@ -62,6 +63,8 @@
 #define NBD_SET_SIZE_BLOCKS    _IO(0xab, 7)
 #define NBD_DISCONNECT          _IO(0xab, 8)
 
+#define NBD_OPT_EXPORT_NAME    (1 << 0)
+
 /* That's all folks */
 
 #define read_sync(fd, buffer, size) nbd_wr_sync(fd, buffer, size, true)
@@ -296,22 +299,27 @@ int nbd_negotiate(int csock, off_t size)
        return 0;
 }
 
-int nbd_receive_negotiate(int csock, off_t *size, size_t *blocksize)
+int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags,
+                          off_t *size, size_t *blocksize)
 {
-       char buf[8 + 8 + 8 + 128];
-       uint64_t magic;
+       char buf[256];
+       uint64_t magic, s;
+       uint16_t tmp;
 
        TRACE("Receiving negotation.");
 
-       if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) {
+       if (read_sync(csock, buf, 8) != 8) {
                LOG("read failed");
                errno = EINVAL;
                return -1;
        }
 
-       magic = be64_to_cpup((uint64_t*)(buf + 8));
-       *size = be64_to_cpup((uint64_t*)(buf + 16));
-       *blocksize = 1024;
+       buf[8] = '\0';
+       if (strlen(buf) == 0) {
+               LOG("server connection closed");
+               errno = EINVAL;
+               return -1;
+       }
 
        TRACE("Magic is %c%c%c%c%c%c%c%c",
              qemu_isprint(buf[0]) ? buf[0] : '.',
@@ -322,8 +330,6 @@ int nbd_receive_negotiate(int csock, off_t *size, size_t *blocksize)
              qemu_isprint(buf[5]) ? buf[5] : '.',
              qemu_isprint(buf[6]) ? buf[6] : '.',
              qemu_isprint(buf[7]) ? buf[7] : '.');
-       TRACE("Magic is 0x%" PRIx64, magic);
-       TRACE("Size is %" PRIu64, *size);
 
        if (memcmp(buf, "NBDMAGIC", 8) != 0) {
                LOG("Invalid magic received");
@@ -331,10 +337,99 @@ int nbd_receive_negotiate(int csock, off_t *size, size_t *blocksize)
                return -1;
        }
 
-       TRACE("Checking magic");
+       if (read_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) {
+               LOG("read failed");
+               errno = EINVAL;
+               return -1;
+       }
+       magic = be64_to_cpu(magic);
+       TRACE("Magic is 0x%" PRIx64, magic);
+
+       if (name) {
+               uint32_t reserved = 0;
+               uint32_t opt;
+               uint32_t namesize;
+
+               TRACE("Checking magic (opts_magic)");
+               if (magic != 0x49484156454F5054LL) {
+                       LOG("Bad magic received");
+                       errno = EINVAL;
+                       return -1;
+               }
+               if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) {
+                       LOG("flags read failed");
+                       errno = EINVAL;
+                       return -1;
+               }
+               *flags = be16_to_cpu(tmp) << 16;
+               /* reserved for future use */
+               if (write_sync(csock, &reserved, sizeof(reserved)) !=
+                   sizeof(reserved)) {
+                       LOG("write failed (reserved)");
+                       errno = EINVAL;
+                       return -1;
+               }
+               /* write the export name */
+               magic = cpu_to_be64(magic);
+               if (write_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) {
+                       LOG("write failed (magic)");
+                       errno = EINVAL;
+                       return -1;
+               }
+               opt = cpu_to_be32(NBD_OPT_EXPORT_NAME);
+               if (write_sync(csock, &opt, sizeof(opt)) != sizeof(opt)) {
+                       LOG("write failed (opt)");
+                       errno = EINVAL;
+                       return -1;
+               }
+               namesize = cpu_to_be32(strlen(name));
+               if (write_sync(csock, &namesize, sizeof(namesize)) !=
+                   sizeof(namesize)) {
+                       LOG("write failed (namesize)");
+                       errno = EINVAL;
+                       return -1;
+               }
+               if (write_sync(csock, (char*)name, strlen(name)) != strlen(name)) {
+                       LOG("write failed (name)");
+                       errno = EINVAL;
+                       return -1;
+               }
+       } else {
+               TRACE("Checking magic (cli_magic)");
+
+               if (magic != 0x00420281861253LL) {
+                       LOG("Bad magic received");
+                       errno = EINVAL;
+                       return -1;
+               }
+       }
+
+       if (read_sync(csock, &s, sizeof(s)) != sizeof(s)) {
+               LOG("read failed");
+               errno = EINVAL;
+               return -1;
+       }
+       *size = be64_to_cpu(s);
+       *blocksize = 1024;
+       TRACE("Size is %" PRIu64, *size);
 
-       if (magic != 0x00420281861253LL) {
-               LOG("Bad magic received");
+       if (!name) {
+               if (read_sync(csock, flags, sizeof(*flags)) != sizeof(*flags)) {
+                       LOG("read failed (flags)");
+                       errno = EINVAL;
+                       return -1;
+               }
+               *flags = be32_to_cpup(flags);
+       } else {
+               if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) {
+                       LOG("read failed (tmp)");
+                       errno = EINVAL;
+                       return -1;
+               }
+               *flags |= be32_to_cpu(tmp);
+       }
+       if (read_sync(csock, &buf, 124) != 124) {
+               LOG("read failed (buf)");
                errno = EINVAL;
                return -1;
        }
@@ -393,7 +488,7 @@ int nbd_disconnect(int fd)
        return 0;
 }
 
-int nbd_client(int fd, int csock)
+int nbd_client(int fd)
 {
        int ret;
        int serrno;
@@ -427,7 +522,7 @@ int nbd_disconnect(int fd)
     return -1;
 }
 
-int nbd_client(int fd, int csock)
+int nbd_client(int fd)
 {
     errno = ENOTSUP;
     return -1;
@@ -494,7 +589,7 @@ static int nbd_receive_request(int csock, struct nbd_request *request)
 
 int nbd_receive_reply(int csock, struct nbd_reply *reply)
 {
-       uint8_t buf[4 + 4 + 8];
+       uint8_t buf[NBD_REPLY_SIZE];
        uint32_t magic;
 
        memset(buf, 0xAA, sizeof(buf));
@@ -561,9 +656,9 @@ int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset,
        if (nbd_receive_request(csock, &request) == -1)
                return -1;
 
-       if (request.len > data_size) {
+       if (request.len + NBD_REPLY_SIZE > data_size) {
                LOG("len (%u) is larger than max len (%u)",
-                   request.len, data_size);
+                   request.len + NBD_REPLY_SIZE, data_size);
                errno = EINVAL;
                return -1;
        }
@@ -593,7 +688,8 @@ int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset,
        case NBD_CMD_READ:
                TRACE("Request type is READ");
 
-               if (bdrv_read(bs, (request.from + dev_offset) / 512, data,
+               if (bdrv_read(bs, (request.from + dev_offset) / 512,
+                             data + NBD_REPLY_SIZE,
                              request.len / 512) == -1) {
                        LOG("reading from file failed");
                        errno = EINVAL;
@@ -603,12 +699,21 @@ int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset,
 
                TRACE("Read %u byte(s)", request.len);
 
-               if (nbd_send_reply(csock, &reply) == -1)
-                       return -1;
+               /* Reply
+                  [ 0 ..  3]    magic   (NBD_REPLY_MAGIC)
+                  [ 4 ..  7]    error   (0 == no error)
+                  [ 7 .. 15]    handle
+                */
+
+               cpu_to_be32w((uint32_t*)data, NBD_REPLY_MAGIC);
+               cpu_to_be32w((uint32_t*)(data + 4), reply.error);
+               cpu_to_be64w((uint64_t*)(data + 8), reply.handle);
 
                TRACE("Sending data to client");
 
-               if (write_sync(csock, data, request.len) != request.len) {
+               if (write_sync(csock, data,
+                              request.len + NBD_REPLY_SIZE) !=
+                              request.len + NBD_REPLY_SIZE) {
                        LOG("writing to socket failed");
                        errno = EINVAL;
                        return -1;