Skip to content

Commit a19933f

Browse files
arcanisaddaleax
authored andcommitted
fs: implement lutimes
PR-URL: #33399 Backport-PR-URL: #35320 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 232f6e1 commit a19933f

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
@@ -2368,6 +2368,38 @@ changes:
23682368

23692369
Synchronous lchown(2). Returns `undefined`.
23702370

2371+
## `fs.lutimes(path, atime, mtime, callback)`
2372+
<!-- YAML
2373+
added: REPLACEME
2374+
-->
2375+
2376+
* `path` {string|Buffer|URL}
2377+
* `atime` {number|string|Date}
2378+
* `mtime` {number|string|Date}
2379+
* `callback` {Function}
2380+
* `err` {Error}
2381+
2382+
Changes the access and modification times of a file in the same way as
2383+
[`fs.utimes()`][], with the difference that if the path refers to a symbolic
2384+
link, then the link is not dereferenced: instead, the timestamps of the
2385+
symbolic link itself are changed.
2386+
2387+
No arguments other than a possible exception are given to the completion
2388+
callback.
2389+
2390+
## `fs.lutimesSync(path, atime, mtime)`
2391+
<!-- YAML
2392+
added: REPLACEME
2393+
-->
2394+
2395+
* `path` {string|Buffer|URL}
2396+
* `atime` {number|string|Date}
2397+
* `mtime` {number|string|Date}
2398+
2399+
Change the file system timestamps of the symbolic link referenced by `path`.
2400+
Returns `undefined`, or throws an exception when parameters are incorrect or
2401+
the operation fails. This is the synchronous version of [`fs.lutimes()`][].
2402+
23712403
## `fs.link(existingPath, newPath, callback)`
23722404
<!-- YAML
23732405
added: v0.1.31
@@ -4946,6 +4978,23 @@ changes:
49464978
Changes the ownership on a symbolic link then resolves the `Promise` with
49474979
no arguments upon success.
49484980

4981+
### `fsPromises.lutimes(path, atime, mtime)`
4982+
<!-- YAML
4983+
added: REPLACEME
4984+
-->
4985+
4986+
* `path` {string|Buffer|URL}
4987+
* `atime` {number|string|Date}
4988+
* `mtime` {number|string|Date}
4989+
* Returns: {Promise}
4990+
4991+
Changes the access and modification times of a file in the same way as
4992+
[`fsPromises.utimes()`][], with the difference that if the path refers to a
4993+
symbolic link, then the link is not dereferenced: instead, the timestamps of
4994+
the symbolic link itself are changed.
4995+
4996+
Upon success, the `Promise` is resolved without arguments.
4997+
49494998
### `fsPromises.link(existingPath, newPath)`
49504999
<!-- YAML
49515000
added: v10.0.0
@@ -5755,6 +5804,7 @@ the file contents.
57555804
[`fs.ftruncate()`]: #fs_fs_ftruncate_fd_len_callback
57565805
[`fs.futimes()`]: #fs_fs_futimes_fd_atime_mtime_callback
57575806
[`fs.lstat()`]: #fs_fs_lstat_path_options_callback
5807+
[`fs.lutimes()`]: #fs_fs_lutimes_path_atime_mtime_callback
57585808
[`fs.mkdir()`]: #fs_fs_mkdir_path_options_callback
57595809
[`fs.mkdtemp()`]: #fs_fs_mkdtemp_prefix_options_callback
57605810
[`fs.open()`]: #fs_fs_open_path_flags_mode_callback
@@ -5778,6 +5828,7 @@ the file contents.
57785828
[`fs.writev()`]: #fs_fs_writev_fd_buffers_position_callback
57795829
[`fsPromises.open()`]: #fs_fspromises_open_path_flags_mode
57805830
[`fsPromises.opendir()`]: #fs_fspromises_opendir_path_options
5831+
[`fsPromises.utimes()`]: #fs_fspromises_utimes_path_atime_mtime
57815832
[`inotify(7)`]: http://man7.org/linux/man-pages/man7/inotify.7.html
57825833
[`kqueue(2)`]: https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
57835834
[`net.Socket`]: net.html#net_class_net_socket

lib/fs.js

+24
Original file line numberDiff line numberDiff line change
@@ -1303,6 +1303,28 @@ function futimesSync(fd, atime, mtime) {
13031303
handleErrorFromBinding(ctx);
13041304
}
13051305

1306+
function lutimes(path, atime, mtime, callback) {
1307+
callback = makeCallback(callback);
1308+
path = getValidatedPath(path);
1309+
1310+
const req = new FSReqCallback();
1311+
req.oncomplete = callback;
1312+
binding.lutimes(pathModule.toNamespacedPath(path),
1313+
toUnixTimestamp(atime),
1314+
toUnixTimestamp(mtime),
1315+
req);
1316+
}
1317+
1318+
function lutimesSync(path, atime, mtime) {
1319+
path = getValidatedPath(path);
1320+
const ctx = { path };
1321+
binding.lutimes(pathModule.toNamespacedPath(path),
1322+
toUnixTimestamp(atime),
1323+
toUnixTimestamp(mtime),
1324+
undefined, ctx);
1325+
handleErrorFromBinding(ctx);
1326+
}
1327+
13061328
function writeAll(fd, isUserFd, buffer, offset, length, callback) {
13071329
// write(fd, buffer, offset, length, position, callback)
13081330
fs.write(fd, buffer, offset, length, null, (writeErr, written) => {
@@ -1938,6 +1960,8 @@ module.exports = fs = {
19381960
linkSync,
19391961
lstat,
19401962
lstatSync,
1963+
lutimes,
1964+
lutimesSync,
19411965
mkdir,
19421966
mkdirSync,
19431967
mkdtemp,

lib/internal/fs/promises.js

+9
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,14 @@ async function futimes(handle, atime, mtime) {
474474
return binding.futimes(handle.fd, atime, mtime, kUsePromises);
475475
}
476476

477+
async function lutimes(path, atime, mtime) {
478+
path = getValidatedPath(path);
479+
return binding.lutimes(pathModule.toNamespacedPath(path),
480+
toUnixTimestamp(atime),
481+
toUnixTimestamp(mtime),
482+
kUsePromises);
483+
}
484+
477485
async function realpath(path, options) {
478486
options = getOptions(options, {});
479487
path = getValidatedPath(path);
@@ -541,6 +549,7 @@ module.exports = {
541549
lchown,
542550
chown,
543551
utimes,
552+
lutimes,
544553
realpath,
545554
mkdtemp,
546555
writeFile,

src/node_file.cc

+30
Original file line numberDiff line numberDiff line change
@@ -2247,6 +2247,35 @@ static void FUTimes(const FunctionCallbackInfo<Value>& args) {
22472247
}
22482248
}
22492249

2250+
static void LUTimes(const FunctionCallbackInfo<Value>& args) {
2251+
Environment* env = Environment::GetCurrent(args);
2252+
2253+
const int argc = args.Length();
2254+
CHECK_GE(argc, 3);
2255+
2256+
BufferValue path(env->isolate(), args[0]);
2257+
CHECK_NOT_NULL(*path);
2258+
2259+
CHECK(args[1]->IsNumber());
2260+
const double atime = args[1].As<Number>()->Value();
2261+
2262+
CHECK(args[2]->IsNumber());
2263+
const double mtime = args[2].As<Number>()->Value();
2264+
2265+
FSReqBase* req_wrap_async = GetReqWrap(env, args[3]);
2266+
if (req_wrap_async != nullptr) { // lutimes(path, atime, mtime, req)
2267+
AsyncCall(env, req_wrap_async, args, "lutime", UTF8, AfterNoArgs,
2268+
uv_fs_lutime, *path, atime, mtime);
2269+
} else { // lutimes(path, atime, mtime, undefined, ctx)
2270+
CHECK_EQ(argc, 5);
2271+
FSReqWrapSync req_wrap_sync;
2272+
FS_SYNC_TRACE_BEGIN(lutimes);
2273+
SyncCall(env, args[4], &req_wrap_sync, "lutime",
2274+
uv_fs_lutime, *path, atime, mtime);
2275+
FS_SYNC_TRACE_END(lutimes);
2276+
}
2277+
}
2278+
22502279
static void Mkdtemp(const FunctionCallbackInfo<Value>& args) {
22512280
Environment* env = Environment::GetCurrent(args);
22522281
Isolate* isolate = env->isolate();
@@ -2329,6 +2358,7 @@ void Initialize(Local<Object> target,
23292358

23302359
env->SetMethod(target, "utimes", UTimes);
23312360
env->SetMethod(target, "futimes", FUTimes);
2361+
env->SetMethod(target, "lutimes", LUTimes);
23322362

23332363
env->SetMethod(target, "mkdtemp", Mkdtemp);
23342364

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)