GeezerVison is showing github.com — fs: optimize `fs.cpSync` js calls · nodejs/node@5e03c17
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: lib/internal/fs/cp/cp-sync.js +31-133Lines changed: 31 additions & 133 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 2 | 2 |
| 3 | 3 | // This file is a modified version of the fs-extra's copySync method. | 4 | 4 |
| 5 | | -const { areIdentical, isSrcSubdir } = require('internal/fs/cp/cp'); | | 5 | +const fsBinding = internalBinding('fs'); | | 6 | +const { isSrcSubdir } = require('internal/fs/cp/cp'); | 6 | 7 | const { codes: { | 7 | | - ERR_FS_CP_DIR_TO_NON_DIR, | 8 | 8 | ERR_FS_CP_EEXIST, | 9 | 9 | ERR_FS_CP_EINVAL, | 10 | | - ERR_FS_CP_FIFO_PIPE, | 11 | | - ERR_FS_CP_NON_DIR_TO_DIR, | 12 | | - ERR_FS_CP_SOCKET, | 13 | 10 | ERR_FS_CP_SYMLINK_TO_SUBDIRECTORY, | 14 | | - ERR_FS_CP_UNKNOWN, | 15 | | - ERR_FS_EISDIR, | 16 | 11 | ERR_INVALID_RETURN_VALUE, | 17 | 12 | } } = require('internal/errors'); | 18 | 13 | const { | 19 | 14 | os: { | 20 | 15 | errno: { | 21 | 16 | EEXIST, | 22 | | - EISDIR, | 23 | 17 | EINVAL, | 24 | | - ENOTDIR, | 25 | 18 | }, | 26 | 19 | }, | 27 | 20 | } = internalBinding('constants'); | 28 | 21 | const { | 29 | 22 | chmodSync, | 30 | 23 | copyFileSync, | 31 | | - existsSync, | 32 | 24 | lstatSync, | 33 | 25 | mkdirSync, | 34 | 26 | opendirSync, | @@ -42,7 +34,6 @@ const { | 42 | 34 | dirname, | 43 | 35 | isAbsolute, | 44 | 36 | join, | 45 | | - parse, | 46 | 37 | resolve, | 47 | 38 | } = require('path'); | 48 | 39 | const { isPromise } = require('util/types'); | @@ -54,152 +45,46 @@ function cpSyncFn(src, dest, opts) { | 54 | 45 | 'node is not recommended'; | 55 | 46 | process.emitWarning(warning, 'TimestampPrecisionWarning'); | 56 | 47 | } | 57 | | - const { srcStat, destStat, skipped } = checkPathsSync(src, dest, opts); | 58 | | - if (skipped) return; | 59 | | - checkParentPathsSync(src, srcStat, dest); | 60 | | - return checkParentDir(destStat, src, dest, opts); | 61 | | -} | 62 | | - | 63 | | -function checkPathsSync(src, dest, opts) { | 64 | 48 | if (opts.filter) { | 65 | 49 | const shouldCopy = opts.filter(src, dest); | 66 | 50 | if (isPromise(shouldCopy)) { | 67 | 51 | throw new ERR_INVALID_RETURN_VALUE('boolean', 'filter', shouldCopy); | 68 | 52 | } | 69 | | - if (!shouldCopy) return { __proto__: null, skipped: true }; | | 53 | + if (!shouldCopy) return; | 70 | 54 | } | 71 | | - const { srcStat, destStat } = getStatsSync(src, dest, opts); | 72 | 55 |
| 73 | | - if (destStat) { | 74 | | - if (areIdentical(srcStat, destStat)) { | 75 | | - throw new ERR_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 | | - throw new ERR_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 | | - throw new ERR_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 | | - throw new ERR_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); | 116 | 57 |
| 117 | | -function getStatsSync(src, dest, opts) { | 118 | | - const statFunc = opts.dereference ? statSync : lstatSync; | 119 | | - const srcStat = statFunc(src, { bigint: true, throwIfNoEntry: true }); | 120 | | - const destStat = statFunc(dest, { bigint: true, throwIfNoEntry: false }); | 121 | | - return { srcStat, destStat }; | | 58 | + return getStats(src, dest, opts); | 122 | 59 | } | 123 | 60 |
| 124 | | -function checkParentPathsSync(src, srcStat, dest) { | 125 | | - const srcParent = resolve(dirname(src)); | 126 | | - const destParent = resolve(dirname(dest)); | 127 | | - if (destParent === srcParent || destParent === parse(destParent).root) return; | 128 | | - const destStat = statSync(destParent, { bigint: true, throwIfNoEntry: false }); | 129 | | - | 130 | | - if (destStat === undefined) { | 131 | | - return; | 132 | | - } | 133 | | - | 134 | | - if (areIdentical(srcStat, destStat)) { | 135 | | - throw new ERR_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 | | - return checkParentPathsSync(src, srcStat, destParent); | 144 | | -} | 145 | | - | 146 | | -function checkParentDir(destStat, src, dest, opts) { | 147 | | - const destParent = dirname(dest); | 148 | | - if (!existsSync(destParent)) mkdirSync(destParent, { recursive: true }); | 149 | | - return getStats(destStat, src, dest, opts); | 150 | | -} | 151 | | - | 152 | | -function getStats(destStat, src, dest, opts) { | | 61 | +function getStats(src, dest, opts) { | | 62 | + // TODO(@anonrig): Avoid making two stat calls. | 153 | 63 | const statSyncFn = opts.dereference ? statSync : lstatSync; | 154 | 64 | const srcStat = statSyncFn(src); | | 65 | + const destStat = statSyncFn(dest, { bigint: true, throwIfNoEntry: false }); | 155 | 66 |
| 156 | 67 | if (srcStat.isDirectory() && opts.recursive) { | 157 | 68 | return onDir(srcStat, destStat, src, dest, opts); | 158 | | - } else if (srcStat.isDirectory()) { | 159 | | - throw new ERR_FS_EISDIR({ | 160 | | - message: `${src} is a directory (not copied)`, | 161 | | - path: src, | 162 | | - syscall: 'cp', | 163 | | - errno: EINVAL, | 164 | | - code: 'EISDIR', | 165 | | - }); | 166 | 69 | } else if (srcStat.isFile() || | 167 | 70 | srcStat.isCharacterDevice() || | 168 | 71 | srcStat.isBlockDevice()) { | 169 | 72 | return onFile(srcStat, destStat, src, dest, opts); | 170 | 73 | } else if (srcStat.isSymbolicLink()) { | 171 | | - return onLink(destStat, src, dest, opts); | 172 | | - } else if (srcStat.isSocket()) { | 173 | | - throw new ERR_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 | | - } else if (srcStat.isFIFO()) { | 181 | | - throw new ERR_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 | + return onLink(destStat, src, dest, opts.verbatimSymlinks); | 188 | 75 | } | 189 | | - throw new ERR_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 | + const assert = require('internal/assert'); | | 79 | + assert.fail('Unreachable code'); | 196 | 80 | } | 197 | 81 |
| 198 | 82 | function onFile(srcStat, destStat, src, dest, opts) { | 199 | 83 | if (!destStat) return copyFile(srcStat, src, dest, opts); | 200 | 84 | return mayCopyFile(srcStat, src, dest, opts); | 201 | 85 | } | 202 | 86 |
| | 87 | +// TODO(@anonrig): Move this function to C++. | 203 | 88 | function mayCopyFile(srcStat, src, dest, opts) { | 204 | 89 | if (opts.force) { | 205 | 90 | unlinkSync(dest); | @@ -249,6 +134,7 @@ function setDestTimestamps(src, dest) { | 249 | 134 | return utimesSync(dest, updatedSrcStat.atime, updatedSrcStat.mtime); | 250 | 135 | } | 251 | 136 |
| | 137 | +// TODO(@anonrig): Move this function to C++. | 252 | 138 | function onDir(srcStat, destStat, src, dest, opts) { | 253 | 139 | if (!destStat) return mkDirAndCopy(srcStat.mode, src, dest, opts); | 254 | 140 | return copyDir(src, dest, opts); | @@ -260,6 +146,7 @@ function mkDirAndCopy(srcMode, src, dest, opts) { | 260 | 146 | return setDestMode(dest, srcMode); | 261 | 147 | } | 262 | 148 |
| | 149 | +// TODO(@anonrig): Move this function to C++. | 263 | 150 | function copyDir(src, dest, opts) { | 264 | 151 | const dir = opendirSync(src); | 265 | 152 |
| @@ -270,17 +157,28 @@ function copyDir(src, dest, opts) { | 270 | 157 | const { name } = dirent; | 271 | 158 | const srcItem = join(src, name); | 272 | 159 | const destItem = join(dest, name); | 273 | | - const { destStat, skipped } = checkPathsSync(srcItem, destItem, opts); | 274 | | - if (!skipped) getStats(destStat, srcItem, destItem, opts); | | 160 | + let shouldCopy = true; | | 161 | + | | 162 | + if (opts.filter) { | | 163 | + shouldCopy = opts.filter(srcItem, destItem); | | 164 | + if (isPromise(shouldCopy)) { | | 165 | + throw new ERR_INVALID_RETURN_VALUE('boolean', 'filter', shouldCopy); | | 166 | + } | | 167 | + } | | 168 | + | | 169 | + if (shouldCopy) { | | 170 | + getStats(srcItem, destItem, opts); | | 171 | + } | 275 | 172 | } | 276 | 173 | } finally { | 277 | 174 | dir.closeSync(); | 278 | 175 | } | 279 | 176 | } | 280 | 177 |
| 281 | | -function onLink(destStat, src, dest, opts) { | | 178 | +// TODO(@anonrig): Move this function to C++. | | 179 | +function onLink(destStat, src, dest, verbatimSymlinks) { | 282 | 180 | let resolvedSrc = readlinkSync(src); | 283 | | - if (!opts.verbatimSymlinks && !isAbsolute(resolvedSrc)) { | | 181 | + if (!verbatimSymlinks && !isAbsolute(resolvedSrc)) { | 284 | 182 | resolvedSrc = resolve(dirname(src), resolvedSrc); | 285 | 183 | } | 286 | 184 | if (!destStat) { | |
Collapse file Copy file name to clipboardExpand all lines: src/node_errors.h +7Lines changed: 7 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -70,6 +70,13 @@ void OOMErrorHandler(const char* location, const v8::OOMDetails& details); | 70 | 70 | V(ERR_DLOPEN_FAILED, Error) \ | 71 | 71 | V(ERR_ENCODING_INVALID_ENCODED_DATA, TypeError) \ | 72 | 72 | 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) \ | 73 | 80 | V(ERR_ILLEGAL_CONSTRUCTOR, Error) \ | 74 | 81 | V(ERR_INVALID_ADDRESS, Error) \ | 75 | 82 | V(ERR_INVALID_ARG_VALUE, TypeError) \ | |
|