mirror of
https://github.com/nodejs/node.git
synced 2025-12-28 07:50:41 +00:00
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: https://github.com/nodejs/node/issues/31361 PR-URL: https://github.com/nodejs/node/pull/31389 Reviewed-By: Richard Lau <riclau@uk.ibm.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Minwoo Jung <nodecorelab@gmail.com> Reviewed-By: David Carlier <devnexen@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
This commit is contained in:
parent
cc0748f509
commit
7864c53629
@ -53,6 +53,7 @@ const pathModule = require('path');
|
||||
const { promisify } = require('internal/util');
|
||||
|
||||
const kHandle = Symbol('kHandle');
|
||||
const kFd = Symbol('kFd');
|
||||
const { kUsePromises } = binding;
|
||||
|
||||
const getDirectoryEntriesPromise = promisify(getDirents);
|
||||
@ -60,6 +61,7 @@ const getDirectoryEntriesPromise = promisify(getDirents);
|
||||
class FileHandle {
|
||||
constructor(filehandle) {
|
||||
this[kHandle] = filehandle;
|
||||
this[kFd] = filehandle.fd;
|
||||
}
|
||||
|
||||
getAsyncId() {
|
||||
@ -67,7 +69,7 @@ class FileHandle {
|
||||
}
|
||||
|
||||
get fd() {
|
||||
return this[kHandle].fd;
|
||||
return this[kFd];
|
||||
}
|
||||
|
||||
appendFile(data, options) {
|
||||
@ -123,6 +125,7 @@ class FileHandle {
|
||||
}
|
||||
|
||||
close = () => {
|
||||
this[kFd] = -1;
|
||||
return this[kHandle].close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,7 +53,6 @@ namespace fs {
|
||||
|
||||
using v8::Array;
|
||||
using v8::Context;
|
||||
using v8::DontDelete;
|
||||
using v8::EscapableHandleScope;
|
||||
using v8::Function;
|
||||
using v8::FunctionCallbackInfo;
|
||||
@ -68,8 +67,6 @@ using v8::Number;
|
||||
using v8::Object;
|
||||
using v8::ObjectTemplate;
|
||||
using v8::Promise;
|
||||
using v8::PropertyAttribute;
|
||||
using v8::ReadOnly;
|
||||
using v8::String;
|
||||
using v8::Symbol;
|
||||
using v8::Uint32;
|
||||
@ -138,15 +135,6 @@ FileHandle* FileHandle::New(Environment* env, int fd, Local<Object> obj) {
|
||||
.ToLocal(&obj)) {
|
||||
return nullptr;
|
||||
}
|
||||
PropertyAttribute attr =
|
||||
static_cast<PropertyAttribute>(ReadOnly | DontDelete);
|
||||
if (obj->DefineOwnProperty(env->context(),
|
||||
env->fd_string(),
|
||||
Integer::New(env->isolate(), fd),
|
||||
attr)
|
||||
.IsNothing()) {
|
||||
return nullptr;
|
||||
}
|
||||
return new FileHandle(env, obj, fd);
|
||||
}
|
||||
|
||||
@ -190,12 +178,13 @@ inline void FileHandle::Close() {
|
||||
uv_fs_t req;
|
||||
int ret = uv_fs_close(env()->event_loop(), &req, fd_, nullptr);
|
||||
uv_fs_req_cleanup(&req);
|
||||
AfterClose();
|
||||
|
||||
struct err_detail { int ret; int fd; };
|
||||
|
||||
err_detail detail { ret, fd_ };
|
||||
|
||||
AfterClose();
|
||||
|
||||
if (ret < 0) {
|
||||
// Do not unref this
|
||||
env()->SetImmediate([detail](Environment* env) {
|
||||
@ -340,6 +329,7 @@ void FileHandle::ReleaseFD(const FunctionCallbackInfo<Value>& args) {
|
||||
void FileHandle::AfterClose() {
|
||||
closing_ = false;
|
||||
closed_ = true;
|
||||
fd_ = -1;
|
||||
if (reading_ && !persistent().IsEmpty())
|
||||
EmitRead(UV_EOF);
|
||||
}
|
||||
|
||||
@ -205,7 +205,7 @@ class FileHandle final : public AsyncWrap, public StreamBase {
|
||||
|
||||
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
int fd() const { return fd_; }
|
||||
int GetFD() override { return fd_; }
|
||||
|
||||
// Will asynchronously close the FD and return a Promise that will
|
||||
// be resolved once closing is complete.
|
||||
|
||||
25
test/parallel/test-fs-filehandle-use-after-close.js
Normal file
25
test/parallel/test-fs-filehandle-use-after-close.js
Normal file
@ -0,0 +1,25 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs').promises;
|
||||
|
||||
(async () => {
|
||||
const filehandle = await fs.open(__filename);
|
||||
|
||||
assert.notStrictEqual(filehandle.fd, -1);
|
||||
await filehandle.close();
|
||||
assert.strictEqual(filehandle.fd, -1);
|
||||
|
||||
// Open another file handle first. This would typically receive the fd
|
||||
// that `filehandle` previously used. In earlier versions of Node.js, the
|
||||
// .stat() call would then succeed because it still used the original fd;
|
||||
// See https://github.com/nodejs/node/issues/31361 for more details.
|
||||
const otherFilehandle = await fs.open(process.execPath);
|
||||
|
||||
assert.rejects(() => filehandle.stat(), {
|
||||
code: 'EBADF',
|
||||
syscall: 'fstat'
|
||||
});
|
||||
|
||||
await otherFilehandle.close();
|
||||
})().then(common.mustCall());
|
||||
Loading…
Reference in New Issue
Block a user