Fix fs can't handle large file on 64bit platform
authorkoichik <koichik@improvement.jp>
Fri, 8 Jul 2011 15:57:57 +0000 (00:57 +0900)
committerkoichik <koichik@improvement.jp>
Wed, 13 Jul 2011 17:52:54 +0000 (02:52 +0900)
fs.read() and fs.write() can't handle more than 2GB files on 64bit platform.
Also fs.truncate() can't handle more than 4GB files.

Fixes #1199.
Fixes #1094.

src/node_file.cc
test/disabled/test-fs-largefile.js [new file with mode: 0644]

index 660e330..e9a0b0c 100644 (file)
@@ -84,6 +84,12 @@ static inline bool SetCloseOnExec(int fd) {
 #endif
 }
 
+#ifdef _LARGEFILE_SOURCE
+static inline int IsInt64(double x) {
+  return x == static_cast<double>(static_cast<int64_t>(x));
+}
+#endif
+
 
 static int After(eio_req *req) {
   HandleScope scope;
@@ -460,6 +466,20 @@ static Handle<Value> Rename(const Arguments& args) {
   }
 }
 
+#ifndef _LARGEFILE_SOURCE
+#define ASSERT_TRUNCATE_LENGTH(a) \
+  if (!(a)->IsUndefined() && !(a)->IsNull() && !(a)->IsUInt32()) { \
+    return ThrowException(Exception::TypeError(String::New("Not an integer"))); \
+  }
+#define GET_TRUNCATE_LENGTH(a) ((a)->UInt32Value())
+#else
+#define ASSERT_TRUNCATE_LENGTH(a) \
+  if (!(a)->IsUndefined() && !(a)->IsNull() && !IsInt64((a)->NumberValue())) { \
+    return ThrowException(Exception::TypeError(String::New("Not an integer"))); \
+  }
+#define GET_TRUNCATE_LENGTH(a) ((a)->IntegerValue())
+#endif
+
 static Handle<Value> Truncate(const Arguments& args) {
   HandleScope scope;
 
@@ -468,7 +488,9 @@ static Handle<Value> Truncate(const Arguments& args) {
   }
 
   int fd = args[0]->Int32Value();
-  off_t len = args[1]->Uint32Value();
+
+  ASSERT_TRUNCATE_LENGTH(args[1]);
+  off_t len = GET_TRUNCATE_LENGTH(args[1]);
 
   if (args[2]->IsFunction()) {
     ASYNC_CALL(ftruncate, args[2], fd, len)
@@ -670,7 +692,19 @@ static Handle<Value> Open(const Arguments& args) {
   }
 }
 
-#define GET_OFFSET(a) (a)->IsInt32() ? (a)->IntegerValue() : -1;
+#ifndef _LARGEFILE_SOURCE
+#define ASSERT_OFFSET(a) \
+  if (!(a)->IsUndefined() && !(a)->IsNull() && !(a)->IsInt32()) { \
+    return ThrowException(Exception::TypeError(String::New("Not an integer"))); \
+  }
+#define GET_OFFSET(a) ((a)->IsNumber() ? (a)->Int32Value() : -1)
+#else
+#define ASSERT_OFFSET(a) \
+  if (!(a)->IsUndefined() && !(a)->IsNull() && !IsInt64((a)->NumberValue())) { \
+    return ThrowException(Exception::TypeError(String::New("Not an integer"))); \
+  }
+#define GET_OFFSET(a) ((a)->IsNumber() ? (a)->IntegerValue() : -1)
+#endif
 
 // bytesWritten = write(fd, data, position, enc, callback)
 // Wrapper for write(2).
@@ -711,6 +745,7 @@ static Handle<Value> Write(const Arguments& args) {
           String::New("Length is extends beyond buffer")));
   }
 
+  ASSERT_OFFSET(args[4]);
   off_t pos = GET_OFFSET(args[4]);
 
   char * buf = (char*)buffer_data + off;
diff --git a/test/disabled/test-fs-largefile.js b/test/disabled/test-fs-largefile.js
new file mode 100644 (file)
index 0000000..83e5879
--- /dev/null
@@ -0,0 +1,55 @@
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+var common = require('../common');
+var assert = require('assert');
+var path = require('path'),
+    fs = require('fs'),
+    filepath = path.join(common.tmpDir, 'large.txt'),
+    fd = fs.openSync(filepath, 'w+'),
+    offset = 5 * 1024 * 1024 * 1024, // 5GB
+    message = 'Large File';
+
+fs.truncateSync(fd, offset);
+assert.equal(fs.statSync(filepath).size, offset);
+var writeBuf = new Buffer(message);
+fs.writeSync(fd, writeBuf, 0, writeBuf.length, offset);
+var readBuf = new Buffer(writeBuf.length);
+fs.readSync(fd, readBuf, 0, readBuf.length, offset);
+assert.equal(readBuf.toString(), message);
+fs.readSync(fd, readBuf, 0, 1, 0);
+assert.equal(readBuf[0], 0);
+
+var exceptionRaised = false;
+try {
+  fs.writeSync(fd, writeBuf, 0, writeBuf.length, 42.000001);
+} catch (err) {
+  console.log(err);
+  exceptionRaised = true;
+  assert.equal(err.message, 'Not an integer');
+}
+assert.ok(exceptionRaised);
+fs.close(fd);
+
+process.on('exit', function() {
+  fs.unlink(filepath);
+});
+