Skip to content

Commit 5e03c17

Browse files
anonrigRafaelGSS
authored andcommitted
fs: optimize fs.cpSync js calls
PR-URL: #53614 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 3999021 commit 5e03c17

5 files changed

Lines changed: 172 additions & 138 deletions

File tree

‎lib/internal/fs/cp/cp-sync.js‎

Lines changed: 31 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,25 @@
22

33
// This file is a modified version of the fs-extra's copySync method.
44

5-
const{ areIdentical, isSrcSubdir }=require('internal/fs/cp/cp');
5+
constfsBinding=internalBinding('fs');
6+
const{ isSrcSubdir }=require('internal/fs/cp/cp');
67
const{codes: {
7-
ERR_FS_CP_DIR_TO_NON_DIR,
88
ERR_FS_CP_EEXIST,
99
ERR_FS_CP_EINVAL,
10-
ERR_FS_CP_FIFO_PIPE,
11-
ERR_FS_CP_NON_DIR_TO_DIR,
12-
ERR_FS_CP_SOCKET,
1310
ERR_FS_CP_SYMLINK_TO_SUBDIRECTORY,
14-
ERR_FS_CP_UNKNOWN,
15-
ERR_FS_EISDIR,
1611
ERR_INVALID_RETURN_VALUE,
1712
}}=require('internal/errors');
1813
const{
1914
os: {
2015
errno: {
2116
EEXIST,
22-
EISDIR,
2317
EINVAL,
24-
ENOTDIR,
2518
},
2619
},
2720
}=internalBinding('constants');
2821
const{
2922
chmodSync,
3023
copyFileSync,
31-
existsSync,
3224
lstatSync,
3325
mkdirSync,
3426
opendirSync,
@@ -42,7 +34,6 @@ const {
4234
dirname,
4335
isAbsolute,
4436
join,
45-
parse,
4637
resolve,
4738
}=require('path');
4839
const{ isPromise }=require('util/types');
@@ -54,152 +45,46 @@ function cpSyncFn(src, dest, opts) {
5445
'node is not recommended';
5546
process.emitWarning(warning,'TimestampPrecisionWarning');
5647
}
57-
const{ srcStat, destStat, skipped }=checkPathsSync(src,dest,opts);
58-
if(skipped)return;
59-
checkParentPathsSync(src,srcStat,dest);
60-
returncheckParentDir(destStat,src,dest,opts);
61-
}
62-
63-
functioncheckPathsSync(src,dest,opts){
6448
if(opts.filter){
6549
constshouldCopy=opts.filter(src,dest);
6650
if(isPromise(shouldCopy)){
6751
thrownewERR_INVALID_RETURN_VALUE('boolean','filter',shouldCopy);
6852
}
69-
if(!shouldCopy)return{__proto__: null,skipped: true};
53+
if(!shouldCopy)return;
7054
}
71-
const{ srcStat, destStat }=getStatsSync(src,dest,opts);
7255

73-
if(destStat){
74-
if(areIdentical(srcStat,destStat)){
75-
thrownewERR_FS_CP_EINVAL({
76-
message: 'src and dest cannot be the same',
77-
path: dest,
78-
syscall: 'cp',
79-
errno: EINVAL,
80-
code: 'EINVAL',
81-
});
82-
}
83-
if(srcStat.isDirectory()&&!destStat.isDirectory()){
84-
thrownewERR_FS_CP_DIR_TO_NON_DIR({
85-
message: `cannot overwrite non-directory ${dest} `+
86-
`with directory ${src}`,
87-
path: dest,
88-
syscall: 'cp',
89-
errno: EISDIR,
90-
code: 'EISDIR',
91-
});
92-
}
93-
if(!srcStat.isDirectory()&&destStat.isDirectory()){
94-
thrownewERR_FS_CP_NON_DIR_TO_DIR({
95-
message: `cannot overwrite directory ${dest} `+
96-
`with non-directory ${src}`,
97-
path: dest,
98-
syscall: 'cp',
99-
errno: ENOTDIR,
100-
code: 'ENOTDIR',
101-
});
102-
}
103-
}
104-
105-
if(srcStat.isDirectory()&&isSrcSubdir(src,dest)){
106-
thrownewERR_FS_CP_EINVAL({
107-
message: `cannot copy ${src} to a subdirectory of self ${dest}`,
108-
path: dest,
109-
syscall: 'cp',
110-
errno: EINVAL,
111-
code: 'EINVAL',
112-
});
113-
}
114-
return{__proto__: null, srcStat, destStat,skipped: false};
115-
}
56+
fsBinding.cpSyncCheckPaths(src,dest,opts.dereference,opts.recursive);
11657

117-
functiongetStatsSync(src,dest,opts){
118-
conststatFunc=opts.dereference ? statSync : lstatSync;
119-
constsrcStat=statFunc(src,{bigint: true,throwIfNoEntry: true});
120-
constdestStat=statFunc(dest,{bigint: true,throwIfNoEntry: false});
121-
return{ srcStat, destStat };
58+
returngetStats(src,dest,opts);
12259
}
12360

124-
functioncheckParentPathsSync(src,srcStat,dest){
125-
constsrcParent=resolve(dirname(src));
126-
constdestParent=resolve(dirname(dest));
127-
if(destParent===srcParent||destParent===parse(destParent).root)return;
128-
constdestStat=statSync(destParent,{bigint: true,throwIfNoEntry: false});
129-
130-
if(destStat===undefined){
131-
return;
132-
}
133-
134-
if(areIdentical(srcStat,destStat)){
135-
thrownewERR_FS_CP_EINVAL({
136-
message: `cannot copy ${src} to a subdirectory of self ${dest}`,
137-
path: dest,
138-
syscall: 'cp',
139-
errno: EINVAL,
140-
code: 'EINVAL',
141-
});
142-
}
143-
returncheckParentPathsSync(src,srcStat,destParent);
144-
}
145-
146-
functioncheckParentDir(destStat,src,dest,opts){
147-
constdestParent=dirname(dest);
148-
if(!existsSync(destParent))mkdirSync(destParent,{recursive: true});
149-
returngetStats(destStat,src,dest,opts);
150-
}
151-
152-
functiongetStats(destStat,src,dest,opts){
61+
functiongetStats(src,dest,opts){
62+
// TODO(@anonrig): Avoid making two stat calls.
15363
conststatSyncFn=opts.dereference ? statSync : lstatSync;
15464
constsrcStat=statSyncFn(src);
65+
constdestStat=statSyncFn(dest,{bigint: true,throwIfNoEntry: false});
15566

15667
if(srcStat.isDirectory()&&opts.recursive){
15768
returnonDir(srcStat,destStat,src,dest,opts);
158-
}elseif(srcStat.isDirectory()){
159-
thrownewERR_FS_EISDIR({
160-
message: `${src} is a directory (not copied)`,
161-
path: src,
162-
syscall: 'cp',
163-
errno: EINVAL,
164-
code: 'EISDIR',
165-
});
16669
}elseif(srcStat.isFile()||
16770
srcStat.isCharacterDevice()||
16871
srcStat.isBlockDevice()){
16972
returnonFile(srcStat,destStat,src,dest,opts);
17073
}elseif(srcStat.isSymbolicLink()){
171-
returnonLink(destStat,src,dest,opts);
172-
}elseif(srcStat.isSocket()){
173-
thrownewERR_FS_CP_SOCKET({
174-
message: `cannot copy a socket file: ${dest}`,
175-
path: dest,
176-
syscall: 'cp',
177-
errno: EINVAL,
178-
code: 'EINVAL',
179-
});
180-
}elseif(srcStat.isFIFO()){
181-
thrownewERR_FS_CP_FIFO_PIPE({
182-
message: `cannot copy a FIFO pipe: ${dest}`,
183-
path: dest,
184-
syscall: 'cp',
185-
errno: EINVAL,
186-
code: 'EINVAL',
187-
});
74+
returnonLink(destStat,src,dest,opts.verbatimSymlinks);
18875
}
189-
thrownewERR_FS_CP_UNKNOWN({
190-
message: `cannot copy an unknown file type: ${dest}`,
191-
path: dest,
192-
syscall: 'cp',
193-
errno: EINVAL,
194-
code: 'EINVAL',
195-
});
76+
77+
// It is not possible to get here because all possible cases are handled above.
78+
constassert=require('internal/assert');
79+
assert.fail('Unreachable code');
19680
}
19781

19882
functiononFile(srcStat,destStat,src,dest,opts){
19983
if(!destStat)returncopyFile(srcStat,src,dest,opts);
20084
returnmayCopyFile(srcStat,src,dest,opts);
20185
}
20286

87+
// TODO(@anonrig): Move this function to C++.
20388
functionmayCopyFile(srcStat,src,dest,opts){
20489
if(opts.force){
20590
unlinkSync(dest);
@@ -249,6 +134,7 @@ function setDestTimestamps(src, dest) {
249134
returnutimesSync(dest,updatedSrcStat.atime,updatedSrcStat.mtime);
250135
}
251136

137+
// TODO(@anonrig): Move this function to C++.
252138
functiononDir(srcStat,destStat,src,dest,opts){
253139
if(!destStat)returnmkDirAndCopy(srcStat.mode,src,dest,opts);
254140
returncopyDir(src,dest,opts);
@@ -260,6 +146,7 @@ function mkDirAndCopy(srcMode, src, dest, opts) {
260146
returnsetDestMode(dest,srcMode);
261147
}
262148

149+
// TODO(@anonrig): Move this function to C++.
263150
functioncopyDir(src,dest,opts){
264151
constdir=opendirSync(src);
265152

@@ -270,17 +157,28 @@ function copyDir(src, dest, opts) {
270157
const{ name }=dirent;
271158
constsrcItem=join(src,name);
272159
constdestItem=join(dest,name);
273-
const{ destStat, skipped }=checkPathsSync(srcItem,destItem,opts);
274-
if(!skipped)getStats(destStat,srcItem,destItem,opts);
160+
letshouldCopy=true;
161+
162+
if(opts.filter){
163+
shouldCopy=opts.filter(srcItem,destItem);
164+
if(isPromise(shouldCopy)){
165+
thrownewERR_INVALID_RETURN_VALUE('boolean','filter',shouldCopy);
166+
}
167+
}
168+
169+
if(shouldCopy){
170+
getStats(srcItem,destItem,opts);
171+
}
275172
}
276173
}finally{
277174
dir.closeSync();
278175
}
279176
}
280177

281-
functiononLink(destStat,src,dest,opts){
178+
// TODO(@anonrig): Move this function to C++.
179+
functiononLink(destStat,src,dest,verbatimSymlinks){
282180
letresolvedSrc=readlinkSync(src);
283-
if(!opts.verbatimSymlinks&&!isAbsolute(resolvedSrc)){
181+
if(!verbatimSymlinks&&!isAbsolute(resolvedSrc)){
284182
resolvedSrc=resolve(dirname(src),resolvedSrc);
285183
}
286184
if(!destStat){

‎src/node_errors.h‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,13 @@ void OOMErrorHandler(const char* location, const v8::OOMDetails& details);
7070
V(ERR_DLOPEN_FAILED, Error) \
7171
V(ERR_ENCODING_INVALID_ENCODED_DATA, TypeError) \
7272
V(ERR_EXECUTION_ENVIRONMENT_NOT_AVAILABLE, Error) \
73+
V(ERR_FS_CP_EINVAL, Error) \
74+
V(ERR_FS_CP_DIR_TO_NON_DIR, Error) \
75+
V(ERR_FS_CP_NON_DIR_TO_DIR, Error) \
76+
V(ERR_FS_EISDIR, Error) \
77+
V(ERR_FS_CP_SOCKET, Error) \
78+
V(ERR_FS_CP_FIFO_PIPE, Error) \
79+
V(ERR_FS_CP_UNKNOWN, Error) \
7380
V(ERR_ILLEGAL_CONSTRUCTOR, Error) \
7481
V(ERR_INVALID_ADDRESS, Error) \
7582
V(ERR_INVALID_ARG_VALUE, TypeError) \

0 commit comments

Comments
 (0)