Skip to content

Commit cf97c56

Browse files
arcaniscodebytere
authored andcommitted
fs: implement lutimes
PR-URL: #33399 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Jiawen Geng <technicalcute@gmail.com> Reviewed-By: Juan José Arboleda <soyjuanarbol@gmail.com>
1 parent 319707a commit cf97c56

5 files changed

Lines changed: 159 additions & 28 deletions

File tree

‎doc/api/fs.md‎

Lines changed: 51 additions & 0 deletions
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‎

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

1328+
functionlutimes(path,atime,mtime,callback){
1329+
callback=makeCallback(callback);
1330+
path=getValidatedPath(path);
1331+
1332+
constreq=newFSReqCallback();
1333+
req.oncomplete=callback;
1334+
binding.lutimes(pathModule.toNamespacedPath(path),
1335+
toUnixTimestamp(atime),
1336+
toUnixTimestamp(mtime),
1337+
req);
1338+
}
1339+
1340+
functionlutimesSync(path,atime,mtime){
1341+
path=getValidatedPath(path);
1342+
constctx={ path };
1343+
binding.lutimes(pathModule.toNamespacedPath(path),
1344+
toUnixTimestamp(atime),
1345+
toUnixTimestamp(mtime),
1346+
undefined,ctx);
1347+
handleErrorFromBinding(ctx);
1348+
}
1349+
13281350
functionwriteAll(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‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,14 @@ async function futimes(handle, atime, mtime) {
510510
returnbinding.futimes(handle.fd,atime,mtime,kUsePromises);
511511
}
512512

513+
asyncfunctionlutimes(path,atime,mtime){
514+
path=getValidatedPath(path);
515+
returnbinding.lutimes(pathModule.toNamespacedPath(path),
516+
toUnixTimestamp(atime),
517+
toUnixTimestamp(mtime),
518+
kUsePromises);
519+
}
520+
513521
asyncfunctionrealpath(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‎

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

2307+
staticvoidLUTimes(const FunctionCallbackInfo<Value>& args) {
2308+
Environment* env = Environment::GetCurrent(args);
2309+
2310+
constint 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+
constdouble atime = args[1].As<Number>()->Value();
2318+
2319+
CHECK(args[2]->IsNumber());
2320+
constdouble 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
staticvoidMkdtemp(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‎

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,17 @@ const common = require('../common');
2424
constassert=require('assert');
2525
constutil=require('util');
2626
constfs=require('fs');
27+
consturl=require('url');
2728

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

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

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

58-
functionexpect_ok(syscall,resource,err,atime,mtime){
59-
constmtime_diff=check_mtime(resource,mtime);
62+
functionexpect_ok(syscall,resource,err,atime,mtime,statSync){
63+
constmtime_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
conststats=fs.statSync(tmpdir.path);
7074

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

8190
functionrunTests(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
letfd;
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
functionsyncTests(){
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
leterr;
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)