Skip to content

Commit 32ac1be

Browse files
addaleaxcodebytere
authored andcommitted
fs: unset FileHandle fd after close
- Do not set the fd as a property on the native object. - Use the already-existent `GetFD()` method to pass the fd from C++ to JS. - Cache the fd in JS to avoid repeated accesses to the C++ getter. - Set the fd to `-1` after close, thus reliably making subsequent calls using the `FileHandle` return `EBADF`. Fixes: #31361 PR-URL: #31389 Reviewed-By: Richard Lau <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Minwoo Jung <[email protected]> Reviewed-By: David Carlier <[email protected]> Reviewed-By: Rich Trott <[email protected]>
1 parent 0bafb5c commit 32ac1be

File tree

4 files changed

+33
-15
lines changed

4 files changed

+33
-15
lines changed

lib/internal/fs/promises.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,21 +52,23 @@ const pathModule = require('path');
5252
const { promisify } = require('internal/util');
5353

5454
const kHandle = Symbol('kHandle');
55+
const kFd = Symbol('kFd');
5556
const { kUsePromises } = binding;
5657

5758
const getDirectoryEntriesPromise = promisify(getDirents);
5859

5960
class FileHandle {
6061
constructor(filehandle) {
6162
this[kHandle] = filehandle;
63+
this[kFd] = filehandle.fd;
6264
}
6365

6466
getAsyncId() {
6567
return this[kHandle].getAsyncId();
6668
}
6769

6870
get fd() {
69-
return this[kHandle].fd;
71+
return this[kFd];
7072
}
7173

7274
appendFile(data, options) {
@@ -122,6 +124,7 @@ class FileHandle {
122124
}
123125

124126
close = () => {
127+
this[kFd] = -1;
125128
return this[kHandle].close();
126129
}
127130
}

src/node_file.cc

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ namespace fs {
5353

5454
using v8::Array;
5555
using v8::Context;
56-
using v8::DontDelete;
5756
using v8::EscapableHandleScope;
5857
using v8::Function;
5958
using v8::FunctionCallbackInfo;
@@ -68,8 +67,6 @@ using v8::Number;
6867
using v8::Object;
6968
using v8::ObjectTemplate;
7069
using v8::Promise;
71-
using v8::PropertyAttribute;
72-
using v8::ReadOnly;
7370
using v8::String;
7471
using v8::Symbol;
7572
using v8::Uint32;
@@ -138,15 +135,6 @@ FileHandle* FileHandle::New(Environment* env, int fd, Local<Object> obj) {
138135
.ToLocal(&obj)) {
139136
return nullptr;
140137
}
141-
PropertyAttribute attr =
142-
static_cast<PropertyAttribute>(ReadOnly | DontDelete);
143-
if (obj->DefineOwnProperty(env->context(),
144-
env->fd_string(),
145-
Integer::New(env->isolate(), fd),
146-
attr)
147-
.IsNothing()) {
148-
return nullptr;
149-
}
150138
return new FileHandle(env, obj, fd);
151139
}
152140

@@ -190,12 +178,13 @@ inline void FileHandle::Close() {
190178
uv_fs_t req;
191179
int ret = uv_fs_close(env()->event_loop(), &req, fd_, nullptr);
192180
uv_fs_req_cleanup(&req);
193-
AfterClose();
194181

195182
struct err_detail { int ret; int fd; };
196183

197184
err_detail detail { ret, fd_ };
198185

186+
AfterClose();
187+
199188
if (ret < 0) {
200189
// Do not unref this
201190
env()->SetImmediate([detail](Environment* env) {
@@ -340,6 +329,7 @@ void FileHandle::ReleaseFD(const FunctionCallbackInfo<Value>& args) {
340329
void FileHandle::AfterClose() {
341330
closing_ = false;
342331
closed_ = true;
332+
fd_ = -1;
343333
if (reading_ && !persistent().IsEmpty())
344334
EmitRead(UV_EOF);
345335
}

src/node_file.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ class FileHandle final : public AsyncWrap, public StreamBase {
205205

206206
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
207207

208-
int fd() const { return fd_; }
208+
int GetFD() override { return fd_; }
209209

210210
// Will asynchronously close the FD and return a Promise that will
211211
// be resolved once closing is complete.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('assert');
4+
const fs = require('fs').promises;
5+
6+
(async () => {
7+
const filehandle = await fs.open(__filename);
8+
9+
assert.notStrictEqual(filehandle.fd, -1);
10+
await filehandle.close();
11+
assert.strictEqual(filehandle.fd, -1);
12+
13+
// Open another file handle first. This would typically receive the fd
14+
// that `filehandle` previously used. In earlier versions of Node.js, the
15+
// .stat() call would then succeed because it still used the original fd;
16+
// See https://github.com/nodejs/node/issues/31361 for more details.
17+
const otherFilehandle = await fs.open(process.execPath);
18+
19+
assert.rejects(() => filehandle.stat(), {
20+
code: 'EBADF',
21+
syscall: 'fstat'
22+
});
23+
24+
await otherFilehandle.close();
25+
})().then(common.mustCall());

0 commit comments

Comments
 (0)