Browse Source

fs.utimes() and fs.futimes() support.

v0.7.4-release
Ben Noordhuis 14 years ago
committed by Ryan Dahl
parent
commit
1d5ff15a46
  1. 12
      doc/api/fs.markdown
  2. 39
      lib/fs.js
  3. 81
      src/node_file.cc
  4. 125
      test/simple/test-fs-utimes.js

12
doc/api/fs.markdown

@ -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`.

39
lib/fs.js

@ -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) {

81
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>
@ -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 */
@ -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;
@ -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");

125
test/simple/test-fs-utimes.js

@ -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);
});
Loading…
Cancel
Save