Skip to content

Commit

Permalink
fs.utimes() and fs.futimes() support.
Browse files Browse the repository at this point in the history
  • Loading branch information
bnoordhuis authored and ry committed Mar 3, 2011
1 parent 8838e14 commit 1d5ff15
Show file tree
Hide file tree
Showing 4 changed files with 257 additions and 0 deletions.
12 changes: 12 additions & 0 deletions doc/api/fs.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,18 @@ or 'a+'. `mode` defaults to 0666. The callback gets two arguments `(err, fd)`.

Synchronous open(2).

### fs.utimes(path, atime, mtime, callback)
### fs.utimesSync(path, atime, mtime)

Change file timestamps.

### fs.futimes(path, atime, mtime, callback)
### fs.futimesSync(path, atime, mtime)

Change file timestamps with the difference that if filename refers to a
symbolic link, then the link is not dereferenced.


### fs.write(fd, buffer, offset, length, position, [callback])

Write `buffer` to the file specified by `fd`.
Expand Down
39 changes: 39 additions & 0 deletions lib/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,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, offset, length, callback) {
// write(fd, buffer, offset, length, position, callback)
fs.write(fd, buffer, offset, length, offset, function(writeErr, written) {
Expand Down
81 changes: 81 additions & 0 deletions src/node_file.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -110,6 +111,11 @@ static int After(eio_req *req) {
argc = 1;
break;

case EIO_UTIME:
case EIO_FUTIME:
argc = 0;
break;

case EIO_OPEN:
SetCloseOnExec(req->result);
/* pass thru */
Expand Down Expand Up @@ -824,6 +830,78 @@ static Handle<Value> Chown(const Arguments& args) {
}
#endif // __POSIX__


// 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;

Expand Down Expand Up @@ -856,6 +934,9 @@ void File::Initialize(Handle<Object> target) {
NODE_SET_METHOD(target, "chown", Chown);
#endif // __POSIX__

NODE_SET_METHOD(target, "utimes", UTimes);
NODE_SET_METHOD(target, "futimes", FUTimes);

errno_symbol = NODE_PSYMBOL("errno");
encoding_symbol = NODE_PSYMBOL("node:encoding");
buf_symbol = NODE_PSYMBOL("__buf");
Expand Down
125 changes: 125 additions & 0 deletions test/simple/test-fs-utimes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
var common = require('../common');
var assert = require('assert');
var util = require('util');
var fs = require('fs');

var tests_ok = 0;
var 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) {
var mtime = fs._toUnixTimestamp(mtime);
var stats = stat_resource(resource);
var 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.code == 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) {

var fd, err;
//
// 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, 'ENOENT');
tests_run++;

err = undefined;
try {
fs.futimesSync(-1, atime, mtime);
} catch (ex) {
err = ex;
}
expect_errno('futimesSync', -1, err, '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, '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, 'EBADF');
syncTests();
callback();
});
tests_run++;
});
tests_run++;
});
tests_run++;
});
tests_run++;
}

var 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);
});

0 comments on commit 1d5ff15

Please sign in to comment.