From b9282b92dfb36082dd47e6471e947bfea2809759 Mon Sep 17 00:00:00 2001 From: Ryan Day Date: Wed, 4 Dec 2019 13:50:39 -0800 Subject: [PATCH] fix: support realpath.native and fix crash in mkdirp --- internal/node/bazel_require_script.js | 41 +++++++++++++- packages/node-patches/BUILD.bazel | 4 +- packages/node-patches/src/fs.ts | 71 +++++++++++++++++-------- packages/node-patches/src/subprocess.ts | 9 +++- 4 files changed, 99 insertions(+), 26 deletions(-) diff --git a/internal/node/bazel_require_script.js b/internal/node/bazel_require_script.js index 626e6687d5..37b58877cc 100644 --- a/internal/node/bazel_require_script.js +++ b/internal/node/bazel_require_script.js @@ -72,12 +72,14 @@ exports.patcher = (fs = fs$1, root) => { } root = fs.realpathSync(root); const origRealpath = fs.realpath.bind(fs); + const origRealpathNative = fs.realpath.native; const origLstat = fs.lstat.bind(fs); const origStat = fs.stat.bind(fs); const origStatSync = fs.statSync.bind(fs); const origReadlink = fs.readlink.bind(fs); const origLstatSync = fs.lstatSync.bind(fs); const origRealpathSync = fs.realpathSync.bind(fs); + const origRealpathSyncNative = fs.realpathSync.native; const origReadlinkSync = fs.readlinkSync.bind(fs); const origReaddir = fs.readdir.bind(fs); const origReaddirSync = fs.readdirSync.bind(fs); @@ -149,6 +151,24 @@ exports.patcher = (fs = fs$1, root) => { } origRealpath(...args); }; + fs.realpath.native = + (...args) => { + let cb = args.length > 1 ? args[args.length - 1] : undefined; + if (cb) { + cb = once(cb); + args[args.length - 1] = (err, str) => { + if (err) + return cb(err); + if (isEscape(str, args[0])) { + cb(false, path.resolve(args[0])); + } + else { + cb(false, str); + } + }; + } + origRealpathNative(...args); + }; // tslint:disable-next-line:no-any fs.readlink = (...args) => { let cb = args.length > 1 ? args[args.length - 1] : undefined; @@ -212,6 +232,14 @@ exports.patcher = (fs = fs$1, root) => { return str; }; // tslint:disable-next-line:no-any + fs.realpathSync = (...args) => { + const str = origRealpathSyncNative(...args); + if (isEscape(str, args[0])) { + return path.resolve(args[0]); + } + return str; + }; + // tslint:disable-next-line:no-any fs.readlinkSync = (...args) => { args[0] = path.resolve(args[0]); const str = path.resolve(path.dirname(args[0]), origReadlinkSync(...args)); @@ -431,7 +459,8 @@ exports.patcher = (fs = fs$1, root) => { // tslint:disable-next-line:no-any const promises = {}; promises.lstat = util.promisify(fs.lstat); - promises.realpath = util.promisify(fs.realpath); + // NOTE: node core uses the newer realpath function fs.promises.native instead of fs.realPath + promises.realpath = util.promisify(fs.realpath.native); promises.readlink = util.promisify(fs.readlink); promises.readdir = util.promisify(fs.readdir); if (fs.opendir) @@ -516,7 +545,15 @@ exports.patcher = (requireScriptName, binDir) => { const nodeDir = path.join(binDir || dir, '_node_bin'); if (!process.env.NP_PATCHED_NODEJS) { // TODO: WINDOWS. - fs$1.mkdirSync(nodeDir, { recursive: true }); + try { + fs$1.mkdirSync(nodeDir, { recursive: true }); + } + catch (e) { + // with node versions that don't have recursive mkdir this may throw an error. + if (e.code !== 'EEXIST') { + throw e; + } + } if (process.platform == 'win32') { fs$1.writeFileSync(path.join(nodeDir, 'node.bat'), `@if not defined DEBUG_HELPER @ECHO OFF set NP_PATCHED_NODEJS=${nodeDir} diff --git a/packages/node-patches/BUILD.bazel b/packages/node-patches/BUILD.bazel index e057515b0b..40ddb40ed8 100644 --- a/packages/node-patches/BUILD.bazel +++ b/packages/node-patches/BUILD.bazel @@ -21,11 +21,11 @@ package(default_visibility = ["//:__subpackages__"]) sources = glob([ "*.ts", "src/*.ts", -]) +],exclude=["**/*.d.ts"]) js = [s.replace(".ts", ".js") for s in sources] -tests = glob(["test/**/*.ts"]) +tests = glob(["test/**/*.ts"],exclude=["**/*.d.ts"]) test_js = [t.replace(".ts", ".js") for t in tests] diff --git a/packages/node-patches/src/fs.ts b/packages/node-patches/src/fs.ts index d14dbc70d4..3be82551be 100644 --- a/packages/node-patches/src/fs.ts +++ b/packages/node-patches/src/fs.ts @@ -42,12 +42,14 @@ export const patcher = (fs: any = _fs, root: string) => { root = fs.realpathSync(root); const origRealpath = fs.realpath.bind(fs); + const origRealpathNative = fs.realpath.native; const origLstat = fs.lstat.bind(fs); const origStat = fs.stat.bind(fs); const origStatSync = fs.statSync.bind(fs); const origReadlink = fs.readlink.bind(fs); const origLstatSync = fs.lstatSync.bind(fs); const origRealpathSync = fs.realpathSync.bind(fs); + const origRealpathSyncNative = fs.realpathSync.native; const origReadlinkSync = fs.readlinkSync.bind(fs); const origReaddir = fs.readdir.bind(fs); const origReaddirSync = fs.readdirSync.bind(fs); @@ -129,29 +131,46 @@ export const patcher = (fs: any = _fs, root: string) => { origRealpath(...args); }; - // tslint:disable-next-line:no-any - fs.readlink = (...args: any[]) => { - let cb = args.length > 1 ? args[args.length - 1] : undefined; - if (cb) { - cb = once(cb); - args[args.length - 1] = (err: Error, str: string) => { - args[0] = path.resolve(args[0]); - if (str) str = path.resolve(path.dirname(args[0]), str); - - if (err) return cb(err); + fs.realpath.native = + (...args) => { + let cb = args.length > 1 ? args[args.length - 1] : undefined; + if (cb) { + cb = once(cb); + args[args.length - 1] = (err: Error, str: string) => { + if (err) return cb(err); + if (isEscape(str, args[0])) { + cb(false, path.resolve(args[0])); + } else { + cb(false, str); + } + }; + } + origRealpathNative(...args) + } - if (isEscape(str, args[0])) { - const e = new Error('EINVAL: invalid argument, readlink \'' + args[0] + '\''); - // tslint:disable-next-line:no-any - (e as any).code = 'EINVAL'; - // if its not supposed to be a link we have to trigger an EINVAL error. - return cb(e); + // tslint:disable-next-line:no-any + fs.readlink = (...args: any[]) => { + let cb = args.length > 1 ? args[args.length - 1] : undefined; + if (cb) { + cb = once(cb); + args[args.length - 1] = (err: Error, str: string) => { + args[0] = path.resolve(args[0]); + if (str) str = path.resolve(path.dirname(args[0]), str); + + if (err) return cb(err); + + if (isEscape(str, args[0])) { + const e = new Error('EINVAL: invalid argument, readlink \'' + args[0] + '\''); + // tslint:disable-next-line:no-any + (e as any).code = 'EINVAL'; + // if its not supposed to be a link we have to trigger an EINVAL error. + return cb(e); + } + cb(false, str); + }; } - cb(false, str); + origReadlink(...args); }; - } - origReadlink(...args); - }; // tslint:disable-next-line:no-any fs.lstatSync = (...args: any[]) => { @@ -192,6 +211,15 @@ export const patcher = (fs: any = _fs, root: string) => { return str; }; + // tslint:disable-next-line:no-any + fs.realpathSync = (...args: any[]) => { + const str = origRealpathSyncNative(...args); + if (isEscape(str, args[0])) { + return path.resolve(args[0]); + } + return str; + }; + // tslint:disable-next-line:no-any fs.readlinkSync = (...args: any[]) => { args[0] = path.resolve(args[0]); @@ -419,7 +447,8 @@ export const patcher = (fs: any = _fs, root: string) => { // tslint:disable-next-line:no-any const promises: any = {}; promises.lstat = util.promisify(fs.lstat); - promises.realpath = util.promisify(fs.realpath); + // NOTE: node core uses the newer realpath function fs.promises.native instead of fs.realPath + promises.realpath = util.promisify(fs.realpath.native); promises.readlink = util.promisify(fs.readlink); promises.readdir = util.promisify(fs.readdir); if (fs.opendir) promises.opendir = util.promisify(fs.opendir); diff --git a/packages/node-patches/src/subprocess.ts b/packages/node-patches/src/subprocess.ts index 2d02c53824..4619a526cf 100644 --- a/packages/node-patches/src/subprocess.ts +++ b/packages/node-patches/src/subprocess.ts @@ -11,7 +11,14 @@ export const patcher = (requireScriptName: string, binDir?: string) => { if (!process.env.NP_PATCHED_NODEJS) { // TODO: WINDOWS. - fs.mkdirSync(nodeDir, {recursive: true}); + try { + fs.mkdirSync(nodeDir, {recursive: true}); + } catch (e) { + // with node versions that don't have recursive mkdir this may throw an error. + if (e.code !== 'EEXIST') { + throw e; + } + } if (process.platform == 'win32') { fs.writeFileSync(path.join(nodeDir, 'node.bat'), `@if not defined DEBUG_HELPER @ECHO OFF set NP_PATCHED_NODEJS=${nodeDir}