Skip to content

Commit cf97c56

Browse files
arcaniscodebytere
authored andcommitted
fs: implement lutimes
PR-URL: #33399 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Jiawen Geng <[email protected]> Reviewed-By: Juan José Arboleda <[email protected]>
1 parent 319707a commit cf97c56

File tree

5 files changed

+159
-28
lines changed

5 files changed

+159
-28
lines changed

doc/api/fs.md

+51
Original file line numberDiff line numberDiff line change
@@ -2392,6 +2392,38 @@ changes:
23922392

23932393
Synchronous lchown(2). Returns `undefined`.
23942394

2395+
## `fs.lutimes(path, atime, mtime, callback)`
2396+
<!-- YAML
2397+
addded: REPLACEME
2398+
-->
2399+
2400+
* `path` {string|Buffer|URL}
2401+
* `atime` {number|string|Date}
2402+
* `mtime` {number|string|Date}
2403+
* `callback` {Function}
2404+
* `err` {Error}
2405+
2406+
Changes the access and modification times of a file in the same way as
2407+
[`fs.utimes()`][], with the difference that if the path refers to a symbolic
2408+
link, then the link is not dereferenced: instead, the timestamps of the
2409+
symbolic link itself are changed.
2410+
2411+
No arguments other than a possible exception are given to the completion
2412+
callback.
2413+
2414+
## `fs.lutimesSync(path, atime, mtime)`
2415+
<!-- YAML
2416+
added: REPLACEME
2417+
-->
2418+
2419+
* `path` {string|Buffer|URL}
2420+
* `atime` {number|string|Date}
2421+
* `mtime` {number|string|Date}
2422+
2423+
Change the file system timestamps of the symbolic link referenced by `path`.
2424+
Returns `undefined`, or throws an exception when parameters are incorrect or
2425+
the operation fails. This is the synchronous version of [`fs.lutimes()`][].
2426+
23952427
## `fs.link(existingPath, newPath, callback)`
23962428
<!-- YAML
23972429
added: v0.1.31
@@ -5011,6 +5043,23 @@ changes:
50115043
Changes the ownership on a symbolic link then resolves the `Promise` with
50125044
no arguments upon success.
50135045

5046+
### `fsPromises.lutimes(path, atime, mtime)`
5047+
<!-- YAML
5048+
added: REPLACEME
5049+
-->
5050+
5051+
* `path` {string|Buffer|URL}
5052+
* `atime` {number|string|Date}
5053+
* `mtime` {number|string|Date}
5054+
* Returns: {Promise}
5055+
5056+
Changes the access and modification times of a file in the same way as
5057+
[`fsPromises.utimes()`][], with the difference that if the path refers to a
5058+
symbolic link, then the link is not dereferenced: instead, the timestamps of
5059+
the symbolic link itself are changed.
5060+
5061+
Upon success, the `Promise` is resolved without arguments.
5062+
50145063
### `fsPromises.link(existingPath, newPath)`
50155064
<!-- YAML
50165065
added: v10.0.0
@@ -5829,6 +5878,7 @@ the file contents.
58295878
[`fs.ftruncate()`]: #fs_fs_ftruncate_fd_len_callback
58305879
[`fs.futimes()`]: #fs_fs_futimes_fd_atime_mtime_callback
58315880
[`fs.lstat()`]: #fs_fs_lstat_path_options_callback
5881+
[`fs.lutimes()`]: #fs_fs_lutimes_path_atime_mtime_callback
58325882
[`fs.mkdir()`]: #fs_fs_mkdir_path_options_callback
58335883
[`fs.mkdtemp()`]: #fs_fs_mkdtemp_prefix_options_callback
58345884
[`fs.open()`]: #fs_fs_open_path_flags_mode_callback
@@ -5852,6 +5902,7 @@ the file contents.
58525902
[`fs.writev()`]: #fs_fs_writev_fd_buffers_position_callback
58535903
[`fsPromises.open()`]: #fs_fspromises_open_path_flags_mode
58545904
[`fsPromises.opendir()`]: #fs_fspromises_opendir_path_options
5905+
[`fsPromises.utimes()`]: #fs_fspromises_utimes_path_atime_mtime
58555906
[`inotify(7)`]: http://man7.org/linux/man-pages/man7/inotify.7.html
58565907
[`kqueue(2)`]: https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
58575908
[`net.Socket`]: net.html#net_class_net_socket

lib/fs.js

+24
Original file line numberDiff line numberDiff line change
@@ -1325,6 +1325,28 @@ function futimesSync(fd, atime, mtime) {
13251325
handleErrorFromBinding(ctx);
13261326
}
13271327

1328+
function lutimes(path, atime, mtime, callback) {
1329+
callback = makeCallback(callback);
1330+
path = getValidatedPath(path);
1331+
1332+
const req = new FSReqCallback();
1333+
req.oncomplete = callback;
1334+
binding.lutimes(pathModule.toNamespacedPath(path),
1335+
toUnixTimestamp(atime),
1336+
toUnixTimestamp(mtime),
1337+
req);
1338+
}
1339+
1340+
function lutimesSync(path, atime, mtime) {
1341+
path = getValidatedPath(path);
1342+
const ctx = { path };
1343+
binding.lutimes(pathModule.toNamespacedPath(path),
1344+
toUnixTimestamp(atime),
1345+
toUnixTimestamp(mtime),
1346+
undefined, ctx);
1347+
handleErrorFromBinding(ctx);
1348+
}
1349+
13281350
function writeAll(fd, isUserFd, buffer, offset, length, callback) {
13291351
// write(fd, buffer, offset, length, position, callback)
13301352
fs.write(fd, buffer, offset, length, null, (writeErr, written) => {
@@ -1971,6 +1993,8 @@ module.exports = fs = {
19711993
linkSync,
19721994
lstat,
19731995
lstatSync,
1996+
lutimes,
1997+
lutimesSync,
19741998
mkdir,
19751999
mkdirSync,
19762000
mkdtemp,

lib/internal/fs/promises.js

+9
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,14 @@ async function futimes(handle, atime, mtime) {
510510
return binding.futimes(handle.fd, atime, mtime, kUsePromises);
511511
}
512512

513+
async function lutimes(path, atime, mtime) {
514+
path = getValidatedPath(path);
515+
return binding.lutimes(pathModule.toNamespacedPath(path),
516+
toUnixTimestamp(atime),
517+
toUnixTimestamp(mtime),
518+
kUsePromises);
519+
}
520+
513521
async function realpath(path, options) {
514522
options = getOptions(options, {});
515523
path = getValidatedPath(path);
@@ -582,6 +590,7 @@ module.exports = {
582590
lchown,
583591
chown,
584592
utimes,
593+
lutimes,
585594
realpath,
586595
mkdtemp,
587596
writeFile,

src/node_file.cc

+30
Original file line numberDiff line numberDiff line change
@@ -2304,6 +2304,35 @@ static void FUTimes(const FunctionCallbackInfo<Value>& args) {
23042304
}
23052305
}
23062306

2307+
static void LUTimes(const FunctionCallbackInfo<Value>& args) {
2308+
Environment* env = Environment::GetCurrent(args);
2309+
2310+
const int argc = args.Length();
2311+
CHECK_GE(argc, 3);
2312+
2313+
BufferValue path(env->isolate(), args[0]);
2314+
CHECK_NOT_NULL(*path);
2315+
2316+
CHECK(args[1]->IsNumber());
2317+
const double atime = args[1].As<Number>()->Value();
2318+
2319+
CHECK(args[2]->IsNumber());
2320+
const double mtime = args[2].As<Number>()->Value();
2321+
2322+
FSReqBase* req_wrap_async = GetReqWrap(args, 3);
2323+
if (req_wrap_async != nullptr) { // lutimes(path, atime, mtime, req)
2324+
AsyncCall(env, req_wrap_async, args, "lutime", UTF8, AfterNoArgs,
2325+
uv_fs_lutime, *path, atime, mtime);
2326+
} else { // lutimes(path, atime, mtime, undefined, ctx)
2327+
CHECK_EQ(argc, 5);
2328+
FSReqWrapSync req_wrap_sync;
2329+
FS_SYNC_TRACE_BEGIN(lutimes);
2330+
SyncCall(env, args[4], &req_wrap_sync, "lutime",
2331+
uv_fs_lutime, *path, atime, mtime);
2332+
FS_SYNC_TRACE_END(lutimes);
2333+
}
2334+
}
2335+
23072336
static void Mkdtemp(const FunctionCallbackInfo<Value>& args) {
23082337
Environment* env = Environment::GetCurrent(args);
23092338
Isolate* isolate = env->isolate();
@@ -2399,6 +2428,7 @@ void Initialize(Local<Object> target,
23992428

24002429
env->SetMethod(target, "utimes", UTimes);
24012430
env->SetMethod(target, "futimes", FUTimes);
2431+
env->SetMethod(target, "lutimes", LUTimes);
24022432

24032433
env->SetMethod(target, "mkdtemp", Mkdtemp);
24042434

test/parallel/test-fs-utimes.js

+45-28
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,17 @@ const common = require('../common');
2424
const assert = require('assert');
2525
const util = require('util');
2626
const fs = require('fs');
27+
const url = require('url');
2728

2829
const tmpdir = require('../common/tmpdir');
2930
tmpdir.refresh();
3031

31-
function stat_resource(resource) {
32+
const lpath = `${tmpdir.path}/symlink`;
33+
fs.symlinkSync('unoent-entry', lpath);
34+
35+
function stat_resource(resource, statSync = fs.statSync) {
3236
if (typeof resource === 'string') {
33-
return fs.statSync(resource);
37+
return statSync(resource);
3438
}
3539
const stats = fs.fstatSync(resource);
3640
// Ensure mtime has been written to disk
@@ -41,9 +45,9 @@ function stat_resource(resource) {
4145
return fs.fstatSync(resource);
4246
}
4347

44-
function check_mtime(resource, mtime) {
48+
function check_mtime(resource, mtime, statSync) {
4549
mtime = fs._toUnixTimestamp(mtime);
46-
const stats = stat_resource(resource);
50+
const stats = stat_resource(resource, statSync);
4751
const real_mtime = fs._toUnixTimestamp(stats.mtime);
4852
return mtime - real_mtime;
4953
}
@@ -55,8 +59,8 @@ function expect_errno(syscall, resource, err, errno) {
5559
);
5660
}
5761

58-
function expect_ok(syscall, resource, err, atime, mtime) {
59-
const mtime_diff = check_mtime(resource, mtime);
62+
function expect_ok(syscall, resource, err, atime, mtime, statSync) {
63+
const mtime_diff = check_mtime(resource, mtime, statSync);
6064
assert(
6165
// Check up to single-second precision.
6266
// Sub-second precision is OS and fs dependant.
@@ -68,45 +72,55 @@ function expect_ok(syscall, resource, err, atime, mtime) {
6872

6973
const stats = fs.statSync(tmpdir.path);
7074

75+
const asPath = (path) => path;
76+
const asUrl = (path) => url.pathToFileURL(path);
77+
7178
const cases = [
72-
new Date('1982-09-10 13:37'),
73-
new Date(),
74-
123456.789,
75-
stats.mtime,
76-
['123456', -1],
77-
new Date('2017-04-08T17:59:38.008Z')
79+
[asPath, new Date('1982-09-10 13:37')],
80+
[asPath, new Date()],
81+
[asPath, 123456.789],
82+
[asPath, stats.mtime],
83+
[asPath, '123456', -1],
84+
[asPath, new Date('2017-04-08T17:59:38.008Z')],
85+
[asUrl, new Date()],
7886
];
87+
7988
runTests(cases.values());
8089

8190
function runTests(iter) {
8291
const { value, done } = iter.next();
8392
if (done) return;
93+
8494
// Support easy setting same or different atime / mtime values.
85-
const [atime, mtime] = Array.isArray(value) ? value : [value, value];
95+
const [pathType, atime, mtime = atime] = value;
8696

8797
let fd;
8898
//
8999
// test async code paths
90100
//
91-
fs.utimes(tmpdir.path, atime, mtime, common.mustCall((err) => {
101+
fs.utimes(pathType(tmpdir.path), atime, mtime, common.mustCall((err) => {
92102
expect_ok('utimes', tmpdir.path, err, atime, mtime);
93103

94-
fs.utimes('foobarbaz', atime, mtime, common.mustCall((err) => {
95-
expect_errno('utimes', 'foobarbaz', err, 'ENOENT');
104+
fs.lutimes(pathType(lpath), atime, mtime, common.mustCall((err) => {
105+
expect_ok('lutimes', lpath, err, atime, mtime, fs.lstatSync);
106+
107+
fs.utimes(pathType('foobarbaz'), atime, mtime, common.mustCall((err) => {
108+
expect_errno('utimes', 'foobarbaz', err, 'ENOENT');
96109

97-
// don't close this fd
98-
if (common.isWindows) {
99-
fd = fs.openSync(tmpdir.path, 'r+');
100-
} else {
101-
fd = fs.openSync(tmpdir.path, 'r');
102-
}
110+
// don't close this fd
111+
if (common.isWindows) {
112+
fd = fs.openSync(tmpdir.path, 'r+');
113+
} else {
114+
fd = fs.openSync(tmpdir.path, 'r');
115+
}
103116

104-
fs.futimes(fd, atime, mtime, common.mustCall((err) => {
105-
expect_ok('futimes', fd, err, atime, mtime);
117+
fs.futimes(fd, atime, mtime, common.mustCall((err) => {
118+
expect_ok('futimes', fd, err, atime, mtime);
106119

107-
syncTests();
120+
syncTests();
108121

109-
setImmediate(common.mustCall(runTests), iter);
122+
setImmediate(common.mustCall(runTests), iter);
123+
}));
110124
}));
111125
}));
112126
}));
@@ -115,9 +129,12 @@ function runTests(iter) {
115129
// test synchronized code paths, these functions throw on failure
116130
//
117131
function syncTests() {
118-
fs.utimesSync(tmpdir.path, atime, mtime);
132+
fs.utimesSync(pathType(tmpdir.path), atime, mtime);
119133
expect_ok('utimesSync', tmpdir.path, undefined, atime, mtime);
120134

135+
fs.lutimesSync(pathType(lpath), atime, mtime);
136+
expect_ok('lutimesSync', lpath, undefined, atime, mtime, fs.lstatSync);
137+
121138
// Some systems don't have futimes
122139
// if there's an error, it should be ENOSYS
123140
try {
@@ -129,7 +146,7 @@ function runTests(iter) {
129146

130147
let err;
131148
try {
132-
fs.utimesSync('foobarbaz', atime, mtime);
149+
fs.utimesSync(pathType('foobarbaz'), atime, mtime);
133150
} catch (ex) {
134151
err = ex;
135152
}

0 commit comments

Comments
 (0)