js: Allow serving files > 4GB in size

Currently, the javascript NBD server only handles files of sizes that
are representable in 32 bits.

Although we can't do full 64-bit offsets with Javascript's number
representation, we should be able to handle up to
Number.MAX_SAFE_INTEGER. This change adds support for using the top-32
bit field in file sizes and read request offsets.

Reported-by: Lei YU <mine260309@gmail.com>
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Change-Id: I41dee58c3913cba49c5cd73ebdd5d6fb56786d25
diff --git a/web/js/nbd.js b/web/js/nbd.js
index 1de4d3b..b58d9aa 100644
--- a/web/js/nbd.js
+++ b/web/js/nbd.js
@@ -195,9 +195,10 @@
                 n += 124;
             var resp = new ArrayBuffer(n);
             var view = new DataView(resp, 0, 10);
-            /* export size. todo: 64 bits? */
-            view.setUint32(0, 0);
-            view.setUint32(4, this.file.size & 0xffffffff);
+            /* export size. */
+            var size = this.file.size;
+            view.setUint32(0, Math.floor(size / (2**32)));
+            view.setUint32(4, size & 0xffffffff);
             /* transmission flags: read-only */
             view.setUint16(8, NBD_FLAG_HAS_FLAGS | NBD_FLAG_READ_ONLY);
             this.ws.send(resp);
@@ -305,17 +306,23 @@
 
     this._handle_cmd_read = function(req)
     {
-        if (req.offset_msB)
+        var offset;
+
+        offset = (req.offset_msB * 2**32) + req.offset_lsB;
+
+        if (offset > Number.MAX_SAFE_INTEGER)
             return ENOSPC;
 
-        if (req.offset_lsB + req.length > file.size)
+        if (offset + req.length > Number.MAX_SAFE_INTEGER)
+            return ENOSPC;
+
+        if (offset + req.length > file.size)
             return ENOSPC;
 
         this._log("read: 0x" + req.length.toString(16) +
-                " bytes, offset 0x" + req.offset_lsB.toString(16));
+                " bytes, offset 0x" + offset.toString(16));
 
-        var blob = this.file.slice(req.offset_lsB,
-                req.offset_lsB + req.length);
+        var blob = this.file.slice(offset, offset + req.length);
         var reader = new FileReader();
 
         reader.onload = (function(ev) {