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).
|
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])
|
### fs.write(fd, buffer, offset, length, position, [callback])
|
||||||
|
|
||||||
Write `buffer` to the file specified by `fd`.
|
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);
|
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) {
|
function writeAll(fd, buffer, offset, length, callback) {
|
||||||
// write(fd, buffer, offset, length, position, callback)
|
// write(fd, buffer, offset, length, position, callback)
|
||||||
fs.write(fd, buffer, offset, length, offset, function(writeErr, written) {
|
fs.write(fd, buffer, offset, length, offset, function(writeErr, written) {
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/time.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -110,6 +111,11 @@ static int After(eio_req *req) {
|
|||||||
argc = 1;
|
argc = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case EIO_UTIME:
|
||||||
|
case EIO_FUTIME:
|
||||||
|
argc = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
case EIO_OPEN:
|
case EIO_OPEN:
|
||||||
SetCloseOnExec(req->result);
|
SetCloseOnExec(req->result);
|
||||||
/* pass thru */
|
/* pass thru */
|
||||||
@ -824,6 +830,78 @@ static Handle<Value> Chown(const Arguments& args) {
|
|||||||
}
|
}
|
||||||
#endif // __POSIX__
|
#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) {
|
void File::Initialize(Handle<Object> target) {
|
||||||
HandleScope scope;
|
HandleScope scope;
|
||||||
|
|
||||||
@ -856,6 +934,9 @@ void File::Initialize(Handle<Object> target) {
|
|||||||
NODE_SET_METHOD(target, "chown", Chown);
|
NODE_SET_METHOD(target, "chown", Chown);
|
||||||
#endif // __POSIX__
|
#endif // __POSIX__
|
||||||
|
|
||||||
|
NODE_SET_METHOD(target, "utimes", UTimes);
|
||||||
|
NODE_SET_METHOD(target, "futimes", FUTimes);
|
||||||
|
|
||||||
errno_symbol = NODE_PSYMBOL("errno");
|
errno_symbol = NODE_PSYMBOL("errno");
|
||||||
encoding_symbol = NODE_PSYMBOL("node:encoding");
|
encoding_symbol = NODE_PSYMBOL("node:encoding");
|
||||||
buf_symbol = NODE_PSYMBOL("__buf");
|
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