Skip to content

Instantly share code, notes, and snippets.

@networkimprov
Created March 3, 2011 03:25
Show Gist options
  • Save networkimprov/852280 to your computer and use it in GitHub Desktop.
Save networkimprov/852280 to your computer and use it in GitHub Desktop.
may need to be rebased...
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