Created
March 3, 2011 03:25
-
-
Save networkimprov/852280 to your computer and use it in GitHub Desktop.
may need to be rebased...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 9f74e3f74ccb35d40ac61b24ad21b11c034df2fa Mon Sep 17 00:00:00 2001 | |
From: Ben Noordhuis <[email protected]> | |
Date: Fri, 29 Oct 2010 12:38:13 +0200 | |
Subject: [PATCH] fs.utimes() and fs.futimes() support. | |
--- | |
lib/fs.js | 39 +++++++++++++ | |
src/node_file.cc | 80 ++++++++++++++++++++++++++ | |
test/simple/test-fs-utimes.js | 123 +++++++++++++++++++++++++++++++++++++++++ | |
3 files changed, 242 insertions(+), 0 deletions(-) | |
create mode 100644 test/simple/test-fs-utimes.js | |
diff --git a/lib/fs.js b/lib/fs.js | |
index c1283dd..1a2e422 100644 | |
--- a/lib/fs.js | |
+++ b/lib/fs.js | |
@@ -388,6 +388,45 @@ fs.chownSync = function(path, uid, gid) { | |
return binding.chown(path, uid, gid); | |
}; | |
+// converts Date or number to a fractional UNIX timestamp | |
+function toUnixTimestamp(time) { | |
+ if (typeof time == 'number') { | |
+ return time; | |
+ } | |
+ if (time instanceof Date) { | |
+ // convert to 123.456 UNIX timestamp | |
+ return time.getTime() / 1000; | |
+ } | |
+ throw new Error("Cannot parse time: " + time); | |
+} | |
+ | |
+// exported for unit tests, not for public consumption | |
+fs._toUnixTimestamp = toUnixTimestamp; | |
+ | |
+fs.utimes = function(path, atime, mtime, callback) { | |
+ atime = toUnixTimestamp(atime); | |
+ mtime = toUnixTimestamp(mtime); | |
+ binding.utimes(path, atime, mtime, callback || noop); | |
+}; | |
+ | |
+fs.utimesSync = function(path, atime, mtime) { | |
+ atime = toUnixTimestamp(atime); | |
+ mtime = toUnixTimestamp(mtime); | |
+ binding.utimes(path, atime, mtime); | |
+}; | |
+ | |
+fs.futimes = function(fd, atime, mtime, callback) { | |
+ atime = toUnixTimestamp(atime); | |
+ mtime = toUnixTimestamp(mtime); | |
+ binding.futimes(fd, atime, mtime, callback || noop); | |
+}; | |
+ | |
+fs.futimesSync = function(fd, atime, mtime) { | |
+ atime = toUnixTimestamp(atime); | |
+ mtime = toUnixTimestamp(mtime); | |
+ binding.futimes(fd, atime, mtime); | |
+}; | |
+ | |
function writeAll (fd, buffer, callback) { | |
fs.write(fd, buffer, 0, buffer.length, null, function (writeErr, written) { | |
if (writeErr) { | |
diff --git a/src/node_file.cc b/src/node_file.cc | |
index efdc64c..2d0beaa 100644 | |
--- a/src/node_file.cc | |
+++ b/src/node_file.cc | |
@@ -6,6 +6,7 @@ | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
+#include <sys/time.h> | |
#include <dirent.h> | |
#include <fcntl.h> | |
#include <stdlib.h> | |
@@ -60,6 +61,8 @@ static int After(eio_req *req) { | |
case EIO_CHMOD: | |
case EIO_CHOWN: | |
case EIO_MKDIR: | |
+ case EIO_UTIME: | |
+ case EIO_FUTIME: | |
argv[0] = ErrnoException(req->errorno, NULL, "", static_cast<const char*>(req->ptr1)); | |
break; | |
default: | |
@@ -82,6 +85,8 @@ static int After(eio_req *req) { | |
case EIO_SYMLINK: | |
case EIO_CHMOD: | |
case EIO_CHOWN: | |
+ case EIO_UTIME: | |
+ case EIO_FUTIME: | |
argc = 0; | |
break; | |
@@ -775,6 +780,78 @@ static Handle<Value> Chown(const Arguments& args) { | |
} | |
} | |
+ | |
+// Utimes() and Futimes() helper function, converts 123.456 timestamps to timevals | |
+static inline void ToTimevals(eio_tstamp atime, | |
+ eio_tstamp mtime, | |
+ timeval times[2]) { | |
+ times[0].tv_sec = atime; | |
+ times[0].tv_usec = 10e5 * (atime - (long) atime); | |
+ times[1].tv_sec = mtime; | |
+ times[1].tv_usec = 10e5 * (mtime - (long) mtime); | |
+} | |
+ | |
+ | |
+static Handle<Value> UTimes(const Arguments& args) { | |
+ HandleScope scope; | |
+ | |
+ if (args.Length() < 3 | |
+ || !args[0]->IsString() | |
+ || !args[1]->IsNumber() | |
+ || !args[2]->IsNumber()) | |
+ { | |
+ return THROW_BAD_ARGS; | |
+ } | |
+ | |
+ const String::Utf8Value path(args[0]->ToString()); | |
+ const eio_tstamp atime = static_cast<eio_tstamp>(args[1]->NumberValue()); | |
+ const eio_tstamp mtime = static_cast<eio_tstamp>(args[2]->NumberValue()); | |
+ | |
+ if (args[3]->IsFunction()) { | |
+ ASYNC_CALL(utime, args[3], *path, atime, mtime); | |
+ } else { | |
+ timeval times[2]; | |
+ | |
+ ToTimevals(atime, mtime, times); | |
+ if (utimes(*path, times) == -1) { | |
+ return ThrowException(ErrnoException(errno, "utimes", "", *path)); | |
+ } | |
+ } | |
+ | |
+ return Undefined(); | |
+} | |
+ | |
+ | |
+static Handle<Value> FUTimes(const Arguments& args) { | |
+ HandleScope scope; | |
+ | |
+ if (args.Length() < 3 | |
+ || !args[0]->IsInt32() | |
+ || !args[1]->IsNumber() | |
+ || !args[2]->IsNumber()) | |
+ { | |
+ return THROW_BAD_ARGS; | |
+ } | |
+ | |
+ const int fd = args[0]->Int32Value(); | |
+ const eio_tstamp atime = static_cast<eio_tstamp>(args[1]->NumberValue()); | |
+ const eio_tstamp mtime = static_cast<eio_tstamp>(args[2]->NumberValue()); | |
+ | |
+ if (args[3]->IsFunction()) { | |
+ ASYNC_CALL(futime, args[3], fd, atime, mtime); | |
+ } else { | |
+ timeval times[2]; | |
+ | |
+ ToTimevals(atime, mtime, times); | |
+ if (futimes(fd, times) == -1) { | |
+ return ThrowException(ErrnoException(errno, "futimes", "", 0)); | |
+ } | |
+ } | |
+ | |
+ return Undefined(); | |
+} | |
+ | |
+ | |
void File::Initialize(Handle<Object> target) { | |
HandleScope scope; | |
@@ -801,6 +878,9 @@ void File::Initialize(Handle<Object> target) { | |
NODE_SET_METHOD(target, "chmod", Chmod); | |
NODE_SET_METHOD(target, "chown", Chown); | |
+ NODE_SET_METHOD(target, "utimes", UTimes); | |
+ NODE_SET_METHOD(target, "futimes", FUTimes); | |
+ | |
errno_symbol = NODE_PSYMBOL("errno"); | |
encoding_symbol = NODE_PSYMBOL("node:encoding"); | |
} | |
diff --git a/test/simple/test-fs-utimes.js b/test/simple/test-fs-utimes.js | |
new file mode 100644 | |
index 0000000..25f3111 | |
--- /dev/null | |
+++ b/test/simple/test-fs-utimes.js | |
@@ -0,0 +1,123 @@ | |
+constants = require('constants'); | |
+common = require('../common'); | |
+assert = common.assert; | |
+util = require('util'); | |
+fs = require('fs'); | |
+ | |
+tests_ok = tests_run = 0; | |
+ | |
+function stat_resource(resource) { | |
+ if (typeof resource == 'string') { | |
+ return fs.statSync(resource); | |
+ } else { | |
+ // ensure mtime has been written to disk | |
+ fs.fsyncSync(resource); | |
+ return fs.fstatSync(resource); | |
+ } | |
+} | |
+ | |
+function check_mtime(resource, mtime) { | |
+ mtime = fs._toUnixTimestamp(mtime); | |
+ stats = stat_resource(resource); | |
+ real_mtime = fs._toUnixTimestamp(stats.mtime); | |
+ // check up to single-second precision | |
+ // sub-second precision is OS and fs dependant | |
+ return Math.floor(mtime) == Math.floor(real_mtime); | |
+} | |
+ | |
+function expect_errno(syscall, resource, err, errno) { | |
+ if (err && err.errno == errno) { | |
+ tests_ok++; | |
+ } else { | |
+ console.log('FAILED:', arguments.callee.name, util.inspect(arguments)); | |
+ } | |
+} | |
+ | |
+function expect_ok(syscall, resource, err, atime, mtime) { | |
+ if (!err && check_mtime(resource, mtime)) { | |
+ tests_ok++; | |
+ } else { | |
+ console.log('FAILED:', arguments.callee.name, util.inspect(arguments)); | |
+ } | |
+} | |
+ | |
+// the tests assume that __filename belongs to the user running the tests | |
+// this should be a fairly safe assumption; testing against a temp file | |
+// would be even better though (node doesn't have such functionality yet) | |
+function runTests(atime, mtime, callback) { | |
+ // | |
+ // test synchronized code paths, these functions throw on failure | |
+ // | |
+ function syncTests() { | |
+ fs.utimesSync(__filename, atime, mtime); | |
+ expect_ok('utimesSync', __filename, undefined, atime, mtime); | |
+ tests_run++; | |
+ | |
+ fs.futimesSync(fd, atime, mtime); | |
+ expect_ok('futimesSync', fd, undefined, atime, mtime); | |
+ tests_run++; | |
+ | |
+ err = undefined; | |
+ try { | |
+ fs.utimesSync('foobarbaz', atime, mtime); | |
+ } catch (ex) { | |
+ err = ex; | |
+ } | |
+ expect_errno('utimesSync', 'foobarbaz', err, constants.ENOENT); | |
+ tests_run++; | |
+ | |
+ err = undefined; | |
+ try { | |
+ fs.futimesSync(-1, atime, mtime); | |
+ } catch (ex) { | |
+ err = ex; | |
+ } | |
+ expect_errno('futimesSync', -1, err, constants.EBADF); | |
+ tests_run++; | |
+ } | |
+ | |
+ // | |
+ // test async code paths | |
+ // | |
+ fs.utimes(__filename, atime, mtime, function(err) { | |
+ expect_ok('utimes', __filename, err, atime, mtime); | |
+ | |
+ fs.utimes('foobarbaz', atime, mtime, function(err) { | |
+ expect_errno('utimes', 'foobarbaz', err, constants.ENOENT); | |
+ | |
+ // don't close this fd | |
+ fd = fs.openSync(__filename, 'r'); | |
+ | |
+ fs.futimes(fd, atime, mtime, function(err) { | |
+ expect_ok('futimes', fd, err, atime, mtime); | |
+ | |
+ fs.futimes(-1, atime, mtime, function(err) { | |
+ expect_errno('futimes', -1, err, constants.EBADF); | |
+ syncTests(); | |
+ callback(); | |
+ }); | |
+ tests_run++; | |
+ }); | |
+ tests_run++; | |
+ }); | |
+ tests_run++; | |
+ }); | |
+ tests_run++; | |
+} | |
+ | |
+stats = fs.statSync(__filename); | |
+ | |
+runTests(new Date('1982-09-10 13:37'), new Date('1982-09-10 13:37'), function() { | |
+ runTests(new Date(), new Date(), function() { | |
+ runTests(1234.5678, 1234.5678, function() { | |
+ runTests(stats.mtime, stats.mtime, function() { | |
+ // done | |
+ }); | |
+ }); | |
+ }); | |
+}); | |
+ | |
+process.on('exit', function() { | |
+ console.log('Tests run / ok:', tests_run, '/', tests_ok); | |
+ assert.equal(tests_ok, tests_run); | |
+}); | |
-- | |
1.7.0.4 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment