Skip to content

Commit fad1793

Browse files
anonrigtargos
authored andcommitted
fs: add fast api for InternalModuleStat
PR-URL: #51344 Reviewed-By: Vinícius Lourenço Claro Cardoso <contact@viniciusl.com.br> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com> Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com> Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent 452011b commit fad1793

File tree

5 files changed

+78
-7
lines changed

5 files changed

+78
-7
lines changed

lib/internal/modules/cjs/loader.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ const {
139139
const assert = require('internal/assert');
140140
const fs = require('fs');
141141
const path = require('path');
142-
const { internalModuleStat } = internalBinding('fs');
142+
const internalFsBinding = internalBinding('fs');
143143
const { safeGetenv } = internalBinding('credentials');
144144
const {
145145
getCjsConditions,
@@ -225,7 +225,7 @@ function stat(filename) {
225225
const result = statCache.get(filename);
226226
if (result !== undefined) { return result; }
227227
}
228-
const result = internalModuleStat(filename);
228+
const result = internalFsBinding.internalModuleStat(filename);
229229
if (statCache !== null && result >= 0) {
230230
// Only set cache when `internalModuleStat(filename)` succeeds.
231231
statCache.set(filename, result);

lib/internal/modules/esm/resolve.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ const {
5454
const { Module: CJSModule } = require('internal/modules/cjs/loader');
5555
const { getConditionsSet } = require('internal/modules/esm/utils');
5656
const packageJsonReader = require('internal/modules/package_json_reader');
57-
const { internalModuleStat } = internalBinding('fs');
57+
const internalFsBinding = internalBinding('fs');
5858

5959
/**
6060
* @typedef {import('internal/modules/esm/package_config.js').PackageConfig} PackageConfig
@@ -246,7 +246,7 @@ function finalizeResolution(resolved, base, preserveSymlinks) {
246246
throw err;
247247
}
248248

249-
const stats = internalModuleStat(toNamespacedPath(StringPrototypeEndsWith(path, '/') ?
249+
const stats = internalFsBinding.internalModuleStat(toNamespacedPath(StringPrototypeEndsWith(path, '/') ?
250250
StringPrototypeSlice(path, -1) : path));
251251

252252
// Check for stats.isDirectory()
@@ -806,8 +806,9 @@ function packageResolve(specifier, base, conditions) {
806806
let packageJSONPath = fileURLToPath(packageJSONUrl);
807807
let lastPath;
808808
do {
809-
const stat = internalModuleStat(toNamespacedPath(StringPrototypeSlice(packageJSONPath, 0,
810-
packageJSONPath.length - 13)));
809+
const stat = internalFsBinding.internalModuleStat(
810+
toNamespacedPath(StringPrototypeSlice(packageJSONPath, 0, packageJSONPath.length - 13)),
811+
);
811812
// Check for !stat.isDirectory()
812813
if (stat !== 1) {
813814
lastPath = packageJSONPath;

src/node_external_reference.h

+6
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ using CFunctionCallbackWithOneByteString =
1515
using CFunctionCallback = void (*)(v8::Local<v8::Value> receiver);
1616
using CFunctionCallbackReturnDouble =
1717
double (*)(v8::Local<v8::Object> receiver);
18+
using CFunctionCallbackReturnInt32 =
19+
int32_t (*)(v8::Local<v8::Object> receiver,
20+
const v8::FastOneByteString& input,
21+
// NOLINTNEXTLINE(runtime/references) This is V8 api.
22+
v8::FastApiCallbackOptions& options);
1823
using CFunctionCallbackValueReturnDouble =
1924
double (*)(v8::Local<v8::Value> receiver);
2025
using CFunctionCallbackWithInt64 = void (*)(v8::Local<v8::Object> receiver,
@@ -55,6 +60,7 @@ class ExternalReferenceRegistry {
5560
V(CFunctionCallback) \
5661
V(CFunctionCallbackWithOneByteString) \
5762
V(CFunctionCallbackReturnDouble) \
63+
V(CFunctionCallbackReturnInt32) \
5864
V(CFunctionCallbackValueReturnDouble) \
5965
V(CFunctionCallbackWithInt64) \
6066
V(CFunctionCallbackWithBool) \

src/node_file.cc

+39-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@
3939
#include "stream_base-inl.h"
4040
#include "string_bytes.h"
4141
#include "uv.h"
42+
#include "v8-fast-api-calls.h"
43+
44+
#include <filesystem>
4245

4346
#if defined(__MINGW32__) || defined(_MSC_VER)
4447
# include <io.h>
@@ -52,6 +55,8 @@ using v8::Array;
5255
using v8::BigInt;
5356
using v8::Context;
5457
using v8::EscapableHandleScope;
58+
using v8::FastApiCallbackOptions;
59+
using v8::FastOneByteString;
5560
using v8::Function;
5661
using v8::FunctionCallbackInfo;
5762
using v8::FunctionTemplate;
@@ -1038,6 +1043,33 @@ static void InternalModuleStat(const FunctionCallbackInfo<Value>& args) {
10381043
args.GetReturnValue().Set(rc);
10391044
}
10401045

1046+
static int32_t FastInternalModuleStat(
1047+
Local<Object> recv,
1048+
const FastOneByteString& input,
1049+
// NOLINTNEXTLINE(runtime/references) This is V8 api.
1050+
FastApiCallbackOptions& options) {
1051+
Environment* env = Environment::GetCurrent(recv->GetCreationContextChecked());
1052+
1053+
auto path = std::filesystem::path(input.data, input.data + input.length);
1054+
if (UNLIKELY(!env->permission()->is_granted(
1055+
env, permission::PermissionScope::kFileSystemRead, path.string()))) {
1056+
options.fallback = true;
1057+
return -1;
1058+
}
1059+
1060+
switch (std::filesystem::status(path).type()) {
1061+
case std::filesystem::file_type::directory:
1062+
return 1;
1063+
case std::filesystem::file_type::regular:
1064+
return 0;
1065+
default:
1066+
return -1;
1067+
}
1068+
}
1069+
1070+
v8::CFunction fast_internal_module_stat_(
1071+
v8::CFunction::Make(FastInternalModuleStat));
1072+
10411073
constexpr bool is_uv_error_except_no_entry(int result) {
10421074
return result < 0 && result != UV_ENOENT;
10431075
}
@@ -3261,7 +3293,11 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data,
32613293
SetMethod(isolate, target, "rmdir", RMDir);
32623294
SetMethod(isolate, target, "mkdir", MKDir);
32633295
SetMethod(isolate, target, "readdir", ReadDir);
3264-
SetMethod(isolate, target, "internalModuleStat", InternalModuleStat);
3296+
SetFastMethod(isolate,
3297+
target,
3298+
"internalModuleStat",
3299+
InternalModuleStat,
3300+
&fast_internal_module_stat_);
32653301
SetMethod(isolate, target, "stat", Stat);
32663302
SetMethod(isolate, target, "lstat", LStat);
32673303
SetMethod(isolate, target, "fstat", FStat);
@@ -3382,6 +3418,8 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
33823418
registry->Register(MKDir);
33833419
registry->Register(ReadDir);
33843420
registry->Register(InternalModuleStat);
3421+
registry->Register(FastInternalModuleStat);
3422+
registry->Register(fast_internal_module_stat_.GetTypeInfo());
33853423
registry->Register(Stat);
33863424
registry->Register(LStat);
33873425
registry->Register(FStat);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Flags: --expose-internals --experimental-permission --allow-fs-read=test/common* --allow-fs-read=tools* --allow-fs-read=test/parallel* --allow-child-process
2+
'use strict';
3+
4+
const common = require('../common');
5+
common.skipIfWorker();
6+
7+
if (!common.hasCrypto) {
8+
common.skip('no crypto');
9+
}
10+
11+
const { internalBinding } = require('internal/test/binding');
12+
const assert = require('node:assert');
13+
const fixtures = require('../common/fixtures');
14+
15+
const blockedFile = fixtures.path('permission', 'deny', 'protected-file.md');
16+
const internalFsBinding = internalBinding('fs');
17+
18+
// Run this inside a for loop to trigger the fast API
19+
for (let i = 0; i < 10_000; i++) {
20+
assert.throws(() => {
21+
internalFsBinding.internalModuleStat(blockedFile);
22+
}, {
23+
code: 'ERR_ACCESS_DENIED',
24+
permission: 'FileSystemRead',
25+
});
26+
}

0 commit comments

Comments
 (0)