diff mbox

[v4,07/11] nbd: Clean up ioctl handling of qemu-nbd -c

Message ID 1463006384-7734-8-git-send-email-eblake@redhat.com
State New
Headers show

Commit Message

Eric Blake May 11, 2016, 10:39 p.m. UTC
The kernel ioctl() interface into NBD is limited to 'unsigned long';
we MUST pass in input with that type (and not int or size_t, as
there may be platform ABIs where the wrong types promote incorrectly
through var-args).  Furthermore, on 32-bit platforms, the kernel
is limited to a maximum export size of 2T (our BLKSIZE of 512 times
a SIZE_BLOCKS constrained by 32 bit unsigned long).

Signed-off-by: Eric Blake <eblake@redhat.com>
---
 nbd/client.c | 20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)
diff mbox

Patch

diff --git a/nbd/client.c b/nbd/client.c
index ae9fdd4..f1afa49 100644
--- a/nbd/client.c
+++ b/nbd/client.c
@@ -593,9 +593,15 @@  fail:
 #ifdef __linux__
 int nbd_init(int fd, QIOChannelSocket *sioc, uint32_t flags, off_t size)
 {
+    unsigned long sectors = size / BDRV_SECTOR_SIZE;
+    if (size / BDRV_SECTOR_SIZE != sectors) {
+        LOG("Export size %lld too large for 32-bit kernel", (long long) size);
+        return -E2BIG;
+    }
+
     TRACE("Setting NBD socket");

-    if (ioctl(fd, NBD_SET_SOCK, sioc->fd) < 0) {
+    if (ioctl(fd, NBD_SET_SOCK, (unsigned long) sioc->fd) < 0) {
         int serrno = errno;
         LOG("Failed to set NBD socket");
         return -serrno;
@@ -603,21 +609,25 @@  int nbd_init(int fd, QIOChannelSocket *sioc, uint32_t flags, off_t size)

     TRACE("Setting block size to %lu", (unsigned long)BDRV_SECTOR_SIZE);

-    if (ioctl(fd, NBD_SET_BLKSIZE, (size_t)BDRV_SECTOR_SIZE) < 0) {
+    if (ioctl(fd, NBD_SET_BLKSIZE, (unsigned long)BDRV_SECTOR_SIZE) < 0) {
         int serrno = errno;
         LOG("Failed setting NBD block size");
         return -serrno;
     }

-    TRACE("Setting size to %zd block(s)", (size_t)(size / BDRV_SECTOR_SIZE));
+    TRACE("Setting size to %lu block(s)", sectors);
+    if (size % BDRV_SECTOR_SIZE) {
+        TRACE("Ignoring trailing %d bytes of export",
+              (int) (size % BDRV_SECTOR_SIZE));
+    }

-    if (ioctl(fd, NBD_SET_SIZE_BLOCKS, (size_t)(size / BDRV_SECTOR_SIZE)) < 0) {
+    if (ioctl(fd, NBD_SET_SIZE_BLOCKS, sectors) < 0) {
         int serrno = errno;
         LOG("Failed setting size (in blocks)");
         return -serrno;
     }

-    if (ioctl(fd, NBD_SET_FLAGS, flags) < 0) {
+    if (ioctl(fd, NBD_SET_FLAGS, (unsigned long) flags) < 0) {
         if (errno == ENOTTY) {
             int read_only = (flags & NBD_FLAG_READ_ONLY) != 0;
             TRACE("Setting readonly attribute");