diff --git a/lib/fs.js b/lib/fs.js index 815018a855cde8..e5a3ff5d3961a0 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -236,7 +236,7 @@ function access(path, mode, callback) { const req = new FSReqCallback(); req.oncomplete = callback; - binding.access(pathModule.toNamespacedPath(path), mode, req); + binding.access(path, mode, req); } /** @@ -248,7 +248,7 @@ function access(path, mode, callback) { */ function accessSync(path, mode) { path = getValidatedPath(path); - binding.access(pathModule.toNamespacedPath(path), mode); + binding.access(path, mode); } /** @@ -296,7 +296,7 @@ function existsSync(path) { return false; } - return binding.existsSync(pathModule.toNamespacedPath(path)); + return binding.existsSync(path); } function readFileAfterOpen(err, fd) { @@ -393,10 +393,7 @@ function readFile(path, options, callback) { const req = new FSReqCallback(); req.context = context; req.oncomplete = readFileAfterOpen; - binding.open(pathModule.toNamespacedPath(path), - flagsNumber, - 0o666, - req); + binding.open(path, flagsNumber, 0o666, req); } function tryStatSync(fd, isUserFd) { @@ -448,7 +445,7 @@ function readFileSync(path, options) { if (options.encoding === 'utf8' || options.encoding === 'utf-8') { if (!isInt32(path)) { - path = pathModule.toNamespacedPath(getValidatedPath(path)); + path = getValidatedPath(path); } return binding.readFileUtf8(path, stringToFlags(options.flag)); } @@ -559,10 +556,7 @@ function open(path, flags, mode, callback) { const req = new FSReqCallback(); req.oncomplete = callback; - binding.open(pathModule.toNamespacedPath(path), - flagsNumber, - mode, - req); + binding.open(path, flagsNumber, mode, req); } /** @@ -576,7 +570,7 @@ function openSync(path, flags, mode) { path = getValidatedPath(path); return binding.open( - pathModule.toNamespacedPath(path), + getValidatedPath(path), stringToFlags(flags), parseFileMode(mode, 'mode', 0o666), ); @@ -597,7 +591,7 @@ function openAsBlob(path, options = kEmptyObject) { // To give ourselves flexibility to maybe return the Blob asynchronously, // this API returns a Promise. path = getValidatedPath(path); - return PromiseResolve(createBlobFromFilePath(pathModule.toNamespacedPath(path), { type })); + return PromiseResolve(createBlobFromFilePath(path, { type })); } /** @@ -1010,9 +1004,7 @@ function rename(oldPath, newPath, callback) { newPath = getValidatedPath(newPath, 'newPath'); const req = new FSReqCallback(); req.oncomplete = callback; - binding.rename(pathModule.toNamespacedPath(oldPath), - pathModule.toNamespacedPath(newPath), - req); + binding.rename(oldPath, newPath, req); } @@ -1024,11 +1016,9 @@ function rename(oldPath, newPath, callback) { * @returns {void} */ function renameSync(oldPath, newPath) { - oldPath = getValidatedPath(oldPath, 'oldPath'); - newPath = getValidatedPath(newPath, 'newPath'); binding.rename( - pathModule.toNamespacedPath(oldPath), - pathModule.toNamespacedPath(newPath), + getValidatedPath(oldPath, 'oldPath'), + getValidatedPath(newPath, 'newPath'), ); } @@ -1341,8 +1331,12 @@ function mkdir(path, options, callback) { const req = new FSReqCallback(); req.oncomplete = callback; - binding.mkdir(pathModule.toNamespacedPath(path), - parseFileMode(mode, 'mode'), recursive, req); + binding.mkdir( + path, + parseFileMode(mode, 'mode'), + recursive, + req, + ); } /** @@ -1369,7 +1363,7 @@ function mkdirSync(path, options) { validateBoolean(recursive, 'options.recursive'); const result = binding.mkdir( - pathModule.toNamespacedPath(path), + path, parseFileMode(mode, 'mode'), recursive, ); @@ -1396,7 +1390,7 @@ function readdirSyncRecursive(basePath, options) { function read(path) { const readdirResult = binding.readdir( - pathModule.toNamespacedPath(path), + path, encoding, withFileTypes, ); @@ -1478,8 +1472,12 @@ function readdir(path, options, callback) { getDirents(path, result, callback); }; } - binding.readdir(pathModule.toNamespacedPath(path), options.encoding, - !!options.withFileTypes, req); + binding.readdir( + path, + options.encoding, + !!options.withFileTypes, + req, + ); } /** @@ -1504,7 +1502,7 @@ function readdirSync(path, options) { } const result = binding.readdir( - pathModule.toNamespacedPath(path), + path, options.encoding, !!options.withFileTypes, ); @@ -1556,7 +1554,7 @@ function lstat(path, options = { bigint: false }, callback) { const req = new FSReqCallback(options.bigint); req.oncomplete = callback; - binding.lstat(pathModule.toNamespacedPath(path), options.bigint, req); + binding.lstat(path, options.bigint, req); } /** @@ -1628,9 +1626,8 @@ function fstatSync(fd, options = { bigint: false }) { * @returns {Stats | undefined} */ function lstatSync(path, options = { bigint: false, throwIfNoEntry: true }) { - path = getValidatedPath(path); const stats = binding.lstat( - pathModule.toNamespacedPath(path), + getValidatedPath(path), options.bigint, undefined, options.throwIfNoEntry, @@ -1653,9 +1650,8 @@ function lstatSync(path, options = { bigint: false, throwIfNoEntry: true }) { * @returns {Stats} */ function statSync(path, options = { bigint: false, throwIfNoEntry: true }) { - path = getValidatedPath(path); const stats = binding.stat( - pathModule.toNamespacedPath(path), + getValidatedPath(path), options.bigint, undefined, options.throwIfNoEntry, @@ -1689,7 +1685,7 @@ function readlink(path, options, callback) { path = getValidatedPath(path, 'oldPath'); const req = new FSReqCallback(); req.oncomplete = callback; - binding.readlink(pathModule.toNamespacedPath(path), options.encoding, req); + binding.readlink(path, options.encoding, req); } /** @@ -1702,10 +1698,7 @@ function readlink(path, options, callback) { function readlinkSync(path, options) { options = getOptions(options); path = getValidatedPath(path, 'oldPath'); - return binding.readlink( - pathModule.toNamespacedPath(path), - options.encoding, - ); + return binding.readlink(path, options.encoding); } /** @@ -1759,8 +1752,12 @@ function symlink(target, path, type_, callback_) { const req = new FSReqCallback(); req.oncomplete = callback; - binding.symlink(destination, - pathModule.toNamespacedPath(path), resolvedFlags, req); + binding.symlink( + destination, + path, + resolvedFlags, + req, + ); }); return; } @@ -1771,7 +1768,7 @@ function symlink(target, path, type_, callback_) { const flags = stringToSymlinkType(type); const req = new FSReqCallback(); req.oncomplete = callback; - binding.symlink(destination, pathModule.toNamespacedPath(path), flags, req); + binding.symlink(destination, path, flags, req); } /** @@ -1804,7 +1801,7 @@ function symlinkSync(target, path, type) { binding.symlink( preprocessSymlinkDestination(target, type, path), - pathModule.toNamespacedPath(path), + path, stringToSymlinkType(type), ); } @@ -1826,9 +1823,7 @@ function link(existingPath, newPath, callback) { const req = new FSReqCallback(); req.oncomplete = callback; - binding.link(pathModule.toNamespacedPath(existingPath), - pathModule.toNamespacedPath(newPath), - req); + binding.link(existingPath, newPath, req); } /** @@ -1843,8 +1838,8 @@ function linkSync(existingPath, newPath) { newPath = getValidatedPath(newPath, 'newPath'); binding.link( - pathModule.toNamespacedPath(existingPath), - pathModule.toNamespacedPath(newPath), + existingPath, + newPath, ); } @@ -1856,10 +1851,9 @@ function linkSync(existingPath, newPath) { */ function unlink(path, callback) { callback = makeCallback(callback); - path = getValidatedPath(path); const req = new FSReqCallback(); req.oncomplete = callback; - binding.unlink(pathModule.toNamespacedPath(path), req); + binding.unlink(getValidatedPath(path), req); } /** @@ -1868,8 +1862,7 @@ function unlink(path, callback) { * @returns {void} */ function unlinkSync(path) { - path = pathModule.toNamespacedPath(getValidatedPath(path)); - binding.unlink(path); + binding.unlink(getValidatedPath(path)); } /** @@ -1960,7 +1953,7 @@ function chmod(path, mode, callback) { const req = new FSReqCallback(); req.oncomplete = callback; - binding.chmod(pathModule.toNamespacedPath(path), mode, req); + binding.chmod(path, mode, req); } /** @@ -1973,10 +1966,7 @@ function chmodSync(path, mode) { path = getValidatedPath(path); mode = parseFileMode(mode, 'mode'); - binding.chmod( - pathModule.toNamespacedPath(path), - mode, - ); + binding.chmod(path, mode); } /** @@ -1994,7 +1984,7 @@ function lchown(path, uid, gid, callback) { validateInteger(gid, 'gid', -1, kMaxUserId); const req = new FSReqCallback(); req.oncomplete = callback; - binding.lchown(pathModule.toNamespacedPath(path), uid, gid, req); + binding.lchown(path, uid, gid, req); } /** @@ -2008,11 +1998,7 @@ function lchownSync(path, uid, gid) { path = getValidatedPath(path); validateInteger(uid, 'uid', -1, kMaxUserId); validateInteger(gid, 'gid', -1, kMaxUserId); - binding.lchown( - pathModule.toNamespacedPath(path), - uid, - gid, - ); + binding.lchown(path, uid, gid); } /** @@ -2064,7 +2050,7 @@ function chown(path, uid, gid, callback) { const req = new FSReqCallback(); req.oncomplete = callback; - binding.chown(pathModule.toNamespacedPath(path), uid, gid, req); + binding.chown(path, uid, gid, req); } /** @@ -2079,11 +2065,7 @@ function chownSync(path, uid, gid) { path = getValidatedPath(path); validateInteger(uid, 'uid', -1, kMaxUserId); validateInteger(gid, 'gid', -1, kMaxUserId); - binding.chown( - pathModule.toNamespacedPath(path), - uid, - gid, - ); + binding.chown(path, uid, gid); } /** @@ -2101,10 +2083,12 @@ function utimes(path, atime, mtime, callback) { const req = new FSReqCallback(); req.oncomplete = callback; - binding.utimes(pathModule.toNamespacedPath(path), - toUnixTimestamp(atime), - toUnixTimestamp(mtime), - req); + binding.utimes( + path, + toUnixTimestamp(atime), + toUnixTimestamp(mtime), + req, + ); } /** @@ -2116,9 +2100,8 @@ function utimes(path, atime, mtime, callback) { * @returns {void} */ function utimesSync(path, atime, mtime) { - path = getValidatedPath(path); binding.utimes( - pathModule.toNamespacedPath(path), + getValidatedPath(path), toUnixTimestamp(atime), toUnixTimestamp(mtime), ); @@ -2175,10 +2158,12 @@ function lutimes(path, atime, mtime, callback) { const req = new FSReqCallback(); req.oncomplete = callback; - binding.lutimes(pathModule.toNamespacedPath(path), - toUnixTimestamp(atime), - toUnixTimestamp(mtime), - req); + binding.lutimes( + path, + toUnixTimestamp(atime), + toUnixTimestamp(mtime), + req, + ); } /** @@ -2190,9 +2175,8 @@ function lutimes(path, atime, mtime, callback) { * @returns {void} */ function lutimesSync(path, atime, mtime) { - path = getValidatedPath(path); binding.lutimes( - pathModule.toNamespacedPath(path), + getValidatedPath(path), toUnixTimestamp(atime), toUnixTimestamp(mtime), ); @@ -2338,7 +2322,8 @@ function writeFileSync(path, data, options) { } return binding.writeFileUtf8( - path, data, + path, + data, stringToFlags(flag), parseFileMode(options.mode, 'mode', 0o666), ); @@ -2654,7 +2639,7 @@ function realpathSync(p, options) { // On windows, check that the root exists. On unix there is no need. if (isWindows) { - const out = binding.lstat(pathModule.toNamespacedPath(base), false, undefined, true /* throwIfNoEntry */); + const out = binding.lstat(base, false, undefined, true /* throwIfNoEntry */); if (out === undefined) { return; } @@ -2696,8 +2681,7 @@ function realpathSync(p, options) { // Use stats array directly to avoid creating an fs.Stats instance just // for our internal use. - const baseLong = pathModule.toNamespacedPath(base); - const stats = binding.lstat(baseLong, true, undefined, true /* throwIfNoEntry */); + const stats = binding.lstat(base, true, undefined, true /* throwIfNoEntry */); if (stats === undefined) { return; } @@ -2721,8 +2705,8 @@ function realpathSync(p, options) { } } if (linkTarget === null) { - binding.stat(baseLong, false, undefined, true); - linkTarget = binding.readlink(baseLong, undefined); + binding.stat(base, false, undefined, true); + linkTarget = binding.readlink(base, undefined); } resolvedLink = pathModule.resolve(previous, linkTarget); @@ -2739,7 +2723,7 @@ function realpathSync(p, options) { // On windows, check that the root exists. On unix there is no need. if (isWindows && !knownHard.has(base)) { - const out = binding.lstat(pathModule.toNamespacedPath(base), false, undefined, true /* throwIfNoEntry */); + const out = binding.lstat(base, false, undefined, true /* throwIfNoEntry */); if (out === undefined) { return; } @@ -2759,9 +2743,8 @@ function realpathSync(p, options) { */ realpathSync.native = (path, options) => { options = getOptions(options); - path = getValidatedPath(path); return binding.realpath( - pathModule.toNamespacedPath(path), + getValidatedPath(path), options.encoding, ); }; @@ -2921,7 +2904,7 @@ realpath.native = (path, options, callback) => { path = getValidatedPath(path); const req = new FSReqCallback(); req.oncomplete = callback; - binding.realpath(pathModule.toNamespacedPath(path), options.encoding, req); + binding.realpath(path, options.encoding, req); }; /** @@ -2977,9 +2960,6 @@ function copyFile(src, dest, mode, callback) { src = getValidatedPath(src, 'src'); dest = getValidatedPath(dest, 'dest'); - - src = pathModule.toNamespacedPath(src); - dest = pathModule.toNamespacedPath(dest); callback = makeCallback(callback); const req = new FSReqCallback(); @@ -2996,12 +2976,9 @@ function copyFile(src, dest, mode, callback) { * @returns {void} */ function copyFileSync(src, dest, mode) { - src = getValidatedPath(src, 'src'); - dest = getValidatedPath(dest, 'dest'); - binding.copyFile( - pathModule.toNamespacedPath(src), - pathModule.toNamespacedPath(dest), + getValidatedPath(src, 'src'), + getValidatedPath(dest, 'dest'), mode, ); } diff --git a/lib/internal/fs/promises.js b/lib/internal/fs/promises.js index 676583ffea5d00..51c0b97d3ab3bc 100644 --- a/lib/internal/fs/promises.js +++ b/lib/internal/fs/promises.js @@ -597,10 +597,8 @@ async function readFileHandle(filehandle, options) { // All of the functions are defined as async in order to ensure that errors // thrown cause promise rejections rather than being thrown synchronously. async function access(path, mode = F_OK) { - path = getValidatedPath(path); - return await PromisePrototypeThen( - binding.access(pathModule.toNamespacedPath(path), mode, kUsePromises), + binding.access(getValidatedPath(path), mode, kUsePromises), undefined, handleErrorFromBinding, ); @@ -614,13 +612,13 @@ async function cp(src, dest, options) { } async function copyFile(src, dest, mode) { - src = getValidatedPath(src, 'src'); - dest = getValidatedPath(dest, 'dest'); return await PromisePrototypeThen( - binding.copyFile(pathModule.toNamespacedPath(src), - pathModule.toNamespacedPath(dest), - mode, - kUsePromises), + binding.copyFile( + getValidatedPath(src, 'src'), + getValidatedPath(dest, 'dest'), + mode, + kUsePromises, + ), undefined, handleErrorFromBinding, ); @@ -633,8 +631,7 @@ async function open(path, flags, mode) { const flagsNumber = stringToFlags(flags); mode = parseFileMode(mode, 'mode', 0o666); return new FileHandle(await PromisePrototypeThen( - binding.openFileHandle(pathModule.toNamespacedPath(path), - flagsNumber, mode, kUsePromises), + binding.openFileHandle(path, flagsNumber, mode, kUsePromises), undefined, handleErrorFromBinding, )); @@ -779,9 +776,7 @@ async function rename(oldPath, newPath) { oldPath = getValidatedPath(oldPath, 'oldPath'); newPath = getValidatedPath(newPath, 'newPath'); return await PromisePrototypeThen( - binding.rename(pathModule.toNamespacedPath(oldPath), - pathModule.toNamespacedPath(newPath), - kUsePromises), + binding.rename(oldPath, newPath, kUsePromises), undefined, handleErrorFromBinding, ); @@ -870,7 +865,7 @@ async function readdirRecursive(originalPath, options) { originalPath, await PromisePrototypeThen( binding.readdir( - pathModule.toNamespacedPath(originalPath), + originalPath, options.encoding, !!options.withFileTypes, kUsePromises, @@ -921,7 +916,7 @@ async function readdirRecursive(originalPath, options) { direntPath, await PromisePrototypeThen( binding.readdir( - pathModule.toNamespacedPath(direntPath), + direntPath, options.encoding, false, kUsePromises, @@ -946,7 +941,7 @@ async function readdir(path, options) { } const result = await PromisePrototypeThen( binding.readdir( - pathModule.toNamespacedPath(path), + path, options.encoding, !!options.withFileTypes, kUsePromises, @@ -963,8 +958,7 @@ async function readlink(path, options) { options = getOptions(options); path = getValidatedPath(path, 'oldPath'); return await PromisePrototypeThen( - binding.readlink(pathModule.toNamespacedPath(path), - options.encoding, kUsePromises), + binding.readlink(path, options.encoding, kUsePromises), undefined, handleErrorFromBinding, ); @@ -993,10 +987,12 @@ async function symlink(target, path, type_) { target = getValidatedPath(target, 'target'); path = getValidatedPath(path); return await PromisePrototypeThen( - binding.symlink(preprocessSymlinkDestination(target, type, path), - pathModule.toNamespacedPath(path), - stringToSymlinkType(type), - kUsePromises), + binding.symlink( + preprocessSymlinkDestination(target, type, path), + path, + stringToSymlinkType(type), + kUsePromises, + ), undefined, handleErrorFromBinding, ); @@ -1012,10 +1008,8 @@ async function fstat(handle, options = { bigint: false }) { } async function lstat(path, options = { bigint: false }) { - path = getValidatedPath(path); const result = await PromisePrototypeThen( - binding.lstat(pathModule.toNamespacedPath(path), - options.bigint, kUsePromises), + binding.lstat(getValidatedPath(path), options.bigint, kUsePromises), undefined, handleErrorFromBinding, ); @@ -1023,10 +1017,8 @@ async function lstat(path, options = { bigint: false }) { } async function stat(path, options = { bigint: false }) { - path = getValidatedPath(path); const result = await PromisePrototypeThen( - binding.stat(pathModule.toNamespacedPath(path), - options.bigint, kUsePromises), + binding.stat(getValidatedPath(path), options.bigint, kUsePromises), undefined, handleErrorFromBinding, ); @@ -1034,10 +1026,8 @@ async function stat(path, options = { bigint: false }) { } async function statfs(path, options = { bigint: false }) { - path = getValidatedPath(path); const result = await PromisePrototypeThen( - binding.statfs(pathModule.toNamespacedPath(path), - options.bigint, kUsePromises), + binding.statfs(path, options.bigint, kUsePromises), undefined, handleErrorFromBinding, ); @@ -1048,18 +1038,15 @@ async function link(existingPath, newPath) { existingPath = getValidatedPath(existingPath, 'existingPath'); newPath = getValidatedPath(newPath, 'newPath'); return await PromisePrototypeThen( - binding.link(pathModule.toNamespacedPath(existingPath), - pathModule.toNamespacedPath(newPath), - kUsePromises), + binding.link(existingPath, newPath, kUsePromises), undefined, handleErrorFromBinding, ); } async function unlink(path) { - path = getValidatedPath(path); return await PromisePrototypeThen( - binding.unlink(pathModule.toNamespacedPath(path), kUsePromises), + binding.unlink(getValidatedPath(path), kUsePromises), undefined, handleErrorFromBinding, ); @@ -1078,7 +1065,7 @@ async function chmod(path, mode) { path = getValidatedPath(path); mode = parseFileMode(mode, 'mode'); return await PromisePrototypeThen( - binding.chmod(pathModule.toNamespacedPath(path), mode, kUsePromises), + binding.chmod(path, mode, kUsePromises), undefined, handleErrorFromBinding, ); @@ -1097,7 +1084,7 @@ async function lchown(path, uid, gid) { validateInteger(uid, 'uid', -1, kMaxUserId); validateInteger(gid, 'gid', -1, kMaxUserId); return await PromisePrototypeThen( - binding.lchown(pathModule.toNamespacedPath(path), uid, gid, kUsePromises), + binding.lchown(path, uid, gid, kUsePromises), undefined, handleErrorFromBinding, ); @@ -1118,7 +1105,7 @@ async function chown(path, uid, gid) { validateInteger(uid, 'uid', -1, kMaxUserId); validateInteger(gid, 'gid', -1, kMaxUserId); return await PromisePrototypeThen( - binding.chown(pathModule.toNamespacedPath(path), uid, gid, kUsePromises), + binding.chown(path, uid, gid, kUsePromises), undefined, handleErrorFromBinding, ); @@ -1127,10 +1114,12 @@ async function chown(path, uid, gid) { async function utimes(path, atime, mtime) { path = getValidatedPath(path); return await PromisePrototypeThen( - binding.utimes(pathModule.toNamespacedPath(path), - toUnixTimestamp(atime), - toUnixTimestamp(mtime), - kUsePromises), + binding.utimes( + path, + toUnixTimestamp(atime), + toUnixTimestamp(mtime), + kUsePromises, + ), undefined, handleErrorFromBinding, ); @@ -1147,12 +1136,13 @@ async function futimes(handle, atime, mtime) { } async function lutimes(path, atime, mtime) { - path = getValidatedPath(path); return await PromisePrototypeThen( - binding.lutimes(pathModule.toNamespacedPath(path), - toUnixTimestamp(atime), - toUnixTimestamp(mtime), - kUsePromises), + binding.lutimes( + getValidatedPath(path), + toUnixTimestamp(atime), + toUnixTimestamp(mtime), + kUsePromises, + ), undefined, handleErrorFromBinding, ); @@ -1160,9 +1150,8 @@ async function lutimes(path, atime, mtime) { async function realpath(path, options) { options = getOptions(options); - path = getValidatedPath(path); return await PromisePrototypeThen( - binding.realpath(pathModule.toNamespacedPath(path), options.encoding, kUsePromises), + binding.realpath(getValidatedPath(path), options.encoding, kUsePromises), undefined, handleErrorFromBinding, ); diff --git a/src/node_blob.cc b/src/node_blob.cc index 970117efc3dc1f..1458eabc1237b8 100644 --- a/src/node_blob.cc +++ b/src/node_blob.cc @@ -8,6 +8,7 @@ #include "node_errors.h" #include "node_external_reference.h" #include "node_file.h" +#include "path.h" #include "permission/permission.h" #include "util.h" #include "v8.h" @@ -98,6 +99,7 @@ void BlobFromFilePath(const FunctionCallbackInfo& args) { CHECK_NOT_NULL(*path); THROW_IF_INSUFFICIENT_PERMISSIONS( env, permission::PermissionScope::kFileSystemRead, path.ToStringView()); + ToNamespacedPath(env, &path); auto entry = DataQueue::CreateFdEntry(env, args[0]); if (entry == nullptr) { return THROW_ERR_INVALID_ARG_VALUE(env, "Unabled to open file as blob"); diff --git a/src/node_file.cc b/src/node_file.cc index 52c6c0e71f711e..5762e492b9165b 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -30,6 +30,7 @@ #include "node_process-inl.h" #include "node_stat_watcher.h" #include "node_url.h" +#include "path.h" #include "permission/permission.h" #include "util-inl.h" @@ -963,6 +964,8 @@ void Access(const FunctionCallbackInfo& args) { THROW_IF_INSUFFICIENT_PERMISSIONS( env, permission::PermissionScope::kFileSystemRead, path.ToStringView()); + ToNamespacedPath(env, &path); + if (argc > 2) { // access(path, mode, req) FSReqBase* req_wrap_async = GetReqWrap(args, 2); CHECK_NOT_NULL(req_wrap_async); @@ -1014,6 +1017,8 @@ static void ExistsSync(const FunctionCallbackInfo& args) { THROW_IF_INSUFFICIENT_PERMISSIONS( env, permission::PermissionScope::kFileSystemRead, path.ToStringView()); + ToNamespacedPath(env, &path); + uv_fs_t req; auto make = OnScopeLeave([&req]() { uv_fs_req_cleanup(&req); }); FS_SYNC_TRACE_BEGIN(access); @@ -1073,6 +1078,8 @@ static void Stat(const FunctionCallbackInfo& args) { THROW_IF_INSUFFICIENT_PERMISSIONS( env, permission::PermissionScope::kFileSystemRead, path.ToStringView()); + ToNamespacedPath(env, &path); + bool use_bigint = args[1]->IsTrue(); if (!args[2]->IsUndefined()) { // stat(path, use_bigint, req) FSReqBase* req_wrap_async = GetReqWrap(args, 2, use_bigint); @@ -1113,6 +1120,8 @@ static void LStat(const FunctionCallbackInfo& args) { BufferValue path(realm->isolate(), args[0]); CHECK_NOT_NULL(*path); + ToNamespacedPath(env, &path); + bool use_bigint = args[1]->IsTrue(); if (!args[2]->IsUndefined()) { // lstat(path, use_bigint, req) FSReqBase* req_wrap_async = GetReqWrap(args, 2, use_bigint); @@ -1247,6 +1256,8 @@ static void Symlink(const FunctionCallbackInfo& args) { THROW_IF_INSUFFICIENT_PERMISSIONS( env, permission::PermissionScope::kFileSystemWrite, path.ToStringView()); + ToNamespacedPath(env, &path); + CHECK(args[2]->IsInt32()); int flags = args[2].As()->Value(); @@ -1292,6 +1303,9 @@ static void Link(const FunctionCallbackInfo& args) { THROW_IF_INSUFFICIENT_PERMISSIONS( env, permission::PermissionScope::kFileSystemWrite, dest_view); + ToNamespacedPath(env, &src); + ToNamespacedPath(env, &dest); + if (argc > 2) { // link(src, dest, req) FSReqBase* req_wrap_async = GetReqWrap(args, 2); FS_ASYNC_TRACE_BEGIN2(UV_FS_LINK, @@ -1322,6 +1336,8 @@ static void ReadLink(const FunctionCallbackInfo& args) { THROW_IF_INSUFFICIENT_PERMISSIONS( env, permission::PermissionScope::kFileSystemRead, path.ToStringView()); + ToNamespacedPath(env, &path); + const enum encoding encoding = ParseEncoding(isolate, args[1], UTF8); if (argc > 2) { // readlink(path, encoding, req) @@ -1370,6 +1386,8 @@ static void Rename(const FunctionCallbackInfo& args) { THROW_IF_INSUFFICIENT_PERMISSIONS( env, permission::PermissionScope::kFileSystemWrite, view_old_path); + ToNamespacedPath(env, &old_path); + BufferValue new_path(isolate, args[1]); CHECK_NOT_NULL(*new_path); THROW_IF_INSUFFICIENT_PERMISSIONS( @@ -1377,6 +1395,8 @@ static void Rename(const FunctionCallbackInfo& args) { permission::PermissionScope::kFileSystemWrite, new_path.ToStringView()); + ToNamespacedPath(env, &new_path); + if (argc > 2) { // rename(old_path, new_path, req) FSReqBase* req_wrap_async = GetReqWrap(args, 2); FS_ASYNC_TRACE_BEGIN2(UV_FS_RENAME, @@ -1485,6 +1505,8 @@ static void Unlink(const FunctionCallbackInfo& args) { THROW_IF_INSUFFICIENT_PERMISSIONS( env, permission::PermissionScope::kFileSystemWrite, path.ToStringView()); + ToNamespacedPath(env, &path); + if (argc > 1) { // unlink(path, req) FSReqBase* req_wrap_async = GetReqWrap(args, 1); CHECK_NOT_NULL(req_wrap_async); @@ -1699,6 +1721,8 @@ static void MKDir(const FunctionCallbackInfo& args) { THROW_IF_INSUFFICIENT_PERMISSIONS( env, permission::PermissionScope::kFileSystemWrite, path.ToStringView()); + ToNamespacedPath(env, &path); + CHECK(args[1]->IsInt32()); const int mode = args[1].As()->Value(); @@ -1753,6 +1777,8 @@ static void RealPath(const FunctionCallbackInfo& args) { BufferValue path(isolate, args[0]); CHECK_NOT_NULL(*path); + ToNamespacedPath(env, &path); + const enum encoding encoding = ParseEncoding(isolate, args[1], UTF8); if (argc > 2) { // realpath(path, encoding, req) @@ -1799,6 +1825,8 @@ static void ReadDir(const FunctionCallbackInfo& args) { THROW_IF_INSUFFICIENT_PERMISSIONS( env, permission::PermissionScope::kFileSystemRead, path.ToStringView()); + ToNamespacedPath(env, &path); + const enum encoding encoding = ParseEncoding(isolate, args[1], UTF8); bool with_types = args[2]->IsTrue(); @@ -1913,6 +1941,8 @@ static void Open(const FunctionCallbackInfo& args) { BufferValue path(env->isolate(), args[0]); CHECK_NOT_NULL(*path); + ToNamespacedPath(env, &path); + CHECK(args[1]->IsInt32()); const int flags = args[1].As()->Value(); @@ -1952,6 +1982,8 @@ static void OpenFileHandle(const FunctionCallbackInfo& args) { BufferValue path(realm->isolate(), args[0]); CHECK_NOT_NULL(*path); + ToNamespacedPath(env, &path); + CHECK(args[1]->IsInt32()); const int flags = args[1].As()->Value(); @@ -1999,11 +2031,15 @@ static void CopyFile(const FunctionCallbackInfo& args) { THROW_IF_INSUFFICIENT_PERMISSIONS( env, permission::PermissionScope::kFileSystemRead, src.ToStringView()); + ToNamespacedPath(env, &src); + BufferValue dest(isolate, args[1]); CHECK_NOT_NULL(*dest); THROW_IF_INSUFFICIENT_PERMISSIONS( env, permission::PermissionScope::kFileSystemWrite, dest.ToStringView()); + ToNamespacedPath(env, &dest); + if (argc > 3) { // copyFile(src, dest, flags, req) FSReqBase* req_wrap_async = GetReqWrap(args, 3); FS_ASYNC_TRACE_BEGIN2(UV_FS_COPYFILE, @@ -2401,6 +2437,8 @@ static void ReadFileUtf8(const FunctionCallbackInfo& args) { CHECK_NOT_NULL(*path); if (CheckOpenPermissions(env, path, flags).IsNothing()) return; + ToNamespacedPath(env, &path); + FS_SYNC_TRACE_BEGIN(open); file = uv_fs_open(nullptr, &req, *path, flags, O_RDONLY, nullptr); FS_SYNC_TRACE_END(open); @@ -2506,6 +2544,8 @@ static void Chmod(const FunctionCallbackInfo& args) { THROW_IF_INSUFFICIENT_PERMISSIONS( env, permission::PermissionScope::kFileSystemWrite, path.ToStringView()); + ToNamespacedPath(env, &path); + CHECK(args[1]->IsInt32()); int mode = args[1].As()->Value(); @@ -2568,6 +2608,8 @@ static void Chown(const FunctionCallbackInfo& args) { THROW_IF_INSUFFICIENT_PERMISSIONS( env, permission::PermissionScope::kFileSystemWrite, path.ToStringView()); + ToNamespacedPath(env, &path); + CHECK(IsSafeJsInt(args[1])); const uv_uid_t uid = static_cast(args[1].As()->Value()); @@ -2634,6 +2676,8 @@ static void LChown(const FunctionCallbackInfo& args) { THROW_IF_INSUFFICIENT_PERMISSIONS( env, permission::PermissionScope::kFileSystemWrite, path.ToStringView()); + ToNamespacedPath(env, &path); + CHECK(IsSafeJsInt(args[1])); const uv_uid_t uid = static_cast(args[1].As()->Value()); @@ -2666,6 +2710,8 @@ static void UTimes(const FunctionCallbackInfo& args) { THROW_IF_INSUFFICIENT_PERMISSIONS( env, permission::PermissionScope::kFileSystemWrite, path.ToStringView()); + ToNamespacedPath(env, &path); + CHECK(args[1]->IsNumber()); const double atime = args[1].As()->Value(); @@ -2729,6 +2775,8 @@ static void LUTimes(const FunctionCallbackInfo& args) { THROW_IF_INSUFFICIENT_PERMISSIONS( env, permission::PermissionScope::kFileSystemWrite, path.ToStringView()); + ToNamespacedPath(env, &path); + CHECK(args[1]->IsNumber()); const double atime = args[1].As()->Value(); diff --git a/src/node_url.cc b/src/node_url.cc index 95d15c78407359..57b22123f96f60 100644 --- a/src/node_url.cc +++ b/src/node_url.cc @@ -6,6 +6,7 @@ #include "node_i18n.h" #include "node_metadata.h" #include "node_process-inl.h" +#include "path.h" #include "util-inl.h" #include "v8-fast-api-calls.h" #include "v8.h" diff --git a/src/path.cc b/src/path.cc index 6a56b750d6e947..b07c257b43eaa2 100644 --- a/src/path.cc +++ b/src/path.cc @@ -3,7 +3,6 @@ #include #include "env-inl.h" #include "node_internals.h" -#include "util.h" namespace node { @@ -268,4 +267,40 @@ std::string PathResolve(Environment* env, } #endif // _WIN32 +constexpr static bool IsWindowsDeviceRoot(const char code) noexcept { + return (code >= 'a' && code <= 'z') || (code >= 'A' && code <= 'Z'); +} + +void ToNamespacedPath(Environment* env, BufferValue* path) { +#ifdef _WIN32 + if (path->length() == 0) return; + auto resolved_path = node::PathResolve(env, {path->ToStringView()}); + + if (resolved_path.size() <= 2) { + path->SetLength(resolved_path.size()); + memcpy(&path, resolved_path.c_str(), resolved_path.size()); + return; + } + + if (resolved_path[0] == '\\') { + // Possible UNC root + if (resolved_path[1] == '\\') { + if (resolved_path[2] != '?' && resolved_path[2] != '.') { + // Matched non-long UNC root, convert the path to a long UNC path + std::string intermediary_path = + "\\\\?\\UNC\\" + resolved_path.substr(2); + path->SetLength(intermediary_path.size()); + memcpy(&path, intermediary_path.c_str(), intermediary_path.size()); + } + } else if (IsWindowsDeviceRoot(resolved_path[0]) && + resolved_path[1] == ':' && resolved_path[2] == '\\') { + // Matched device root, convert the path to a long UNC path + std::string intermediary_path = "\\\\?\\" + resolved_path; + path->SetLength(intermediary_path.size()); + memcpy(&path, intermediary_path.c_str(), intermediary_path.size()); + } + } +#endif +} + } // namespace node diff --git a/src/path.h b/src/path.h index 532c5f5849652c..95b11a9dc4c5b2 100644 --- a/src/path.h +++ b/src/path.h @@ -5,11 +5,11 @@ #include #include +#include "node_options-inl.h" +#include "util-inl.h" namespace node { -class Environment; - bool IsPathSeparator(const char c) noexcept; std::string NormalizeString(const std::string_view path, @@ -17,7 +17,14 @@ std::string NormalizeString(const std::string_view path, const std::string_view separator); std::string PathResolve(Environment* env, - const std::vector& args); + const std::vector& paths); + +#ifdef _WIN32 +constexpr static bool IsWindowsDeviceRoot(const char code) noexcept; +#endif // _WIN32 + +void ToNamespacedPath(Environment* env, BufferValue* path); + } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/test/cctest/test_path.cc b/test/cctest/test_path.cc index 44f3b30ddff341..3423055abf74e3 100644 --- a/test/cctest/test_path.cc +++ b/test/cctest/test_path.cc @@ -45,3 +45,22 @@ TEST_F(PathTest, PathResolve) { "/foo/tmp.3/cycles/root.js"); #endif } + +TEST_F(PathTest, ToNamespacedPath) { + const v8::HandleScope handle_scope(isolate_); + Argv argv; + Env env{handle_scope, argv, node::EnvironmentFlags::kNoBrowserGlobals}; + auto cwd = (*env)->GetCwd((*env)->exec_path()); +#ifdef _WIN32 + BufferValue data = BufferValue::New(isolate_, ""); + ToNamespacedPath(*env, &data); + EXPECT_EQ(data.out(), ""); // Empty string should not be mutated + BufferValue data_2 = BufferValue::New(isolate_, "c:"); + EXPECT_EQ(data_2.out(), "c:"); // Input less than equal to 2 characters + // should be returned directly +#else + BufferValue data = BufferValue::New(isolate_, "hello world"); + ToNamespacedPath(*env, &data); + EXPECT_EQ(data.out(), "hello world"); // Input should not be mutated +#endif +}