fs.utimes() and fs.futimes() support.
This commit is contained in:
parent
8838e14ac0
commit
1d5ff15a46
@ -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
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) {
|
||||
|
@ -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
Normal file
125
test/simple/test-fs-utimes.js
Normal file
@ -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…
x
Reference in New Issue
Block a user