Skip to content

Commit 135baa3

Browse files
authored
Fix dup in nodefs by refcounting nodefs file descriptors (#21399)
1 parent b69e44c commit 135baa3

File tree

5 files changed

+66
-4
lines changed

5 files changed

+66
-4
lines changed

src/library_fs.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,11 @@ FS.staticInit();` +
471471
closeStream(fd) {
472472
FS.streams[fd] = null;
473473
},
474+
dupStream(origStream, fd = -1) {
475+
var stream = FS.createStream(origStream, fd);
476+
stream.stream_ops?.dup?.(stream);
477+
return stream;
478+
},
474479

475480
//
476481
// devices

src/library_nodefs.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ addToLibrary({
251251
var path = NODEFS.realPath(stream.node);
252252
try {
253253
if (FS.isFile(stream.node.mode)) {
254+
stream.shared.refcount = 1;
254255
stream.nfd = fs.openSync(path, NODEFS.flagsForNode(stream.flags));
255256
}
256257
} catch (e) {
@@ -260,14 +261,17 @@ addToLibrary({
260261
},
261262
close(stream) {
262263
try {
263-
if (FS.isFile(stream.node.mode) && stream.nfd) {
264+
if (FS.isFile(stream.node.mode) && stream.nfd && --stream.shared.refcount === 0) {
264265
fs.closeSync(stream.nfd);
265266
}
266267
} catch (e) {
267268
if (!e.code) throw e;
268269
throw new FS.ErrnoError(NODEFS.convertNodeCode(e));
269270
}
270271
},
272+
dup(stream) {
273+
stream.shared.refcount++;
274+
},
271275
read(stream, buffer, offset, length, position) {
272276
// Node.js < 6 compatibility: node errors on 0 length reads
273277
if (length === 0) return 0;

src/library_syscall.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ var SyscallsLibrary = {
183183
},
184184
__syscall_dup: (fd) => {
185185
var old = SYSCALLS.getStreamFromFD(fd);
186-
return FS.createStream(old).fd;
186+
return FS.dupStream(old).fd;
187187
},
188188
__syscall_pipe__deps: ['$PIPEFS'],
189189
__syscall_pipe: (fdPtr) => {
@@ -760,7 +760,7 @@ var SyscallsLibrary = {
760760
arg++;
761761
}
762762
var newStream;
763-
newStream = FS.createStream(stream, arg);
763+
newStream = FS.dupStream(stream, arg);
764764
return newStream.fd;
765765
}
766766
case {{{ cDefs.F_GETFD }}}:
@@ -1007,7 +1007,7 @@ var SyscallsLibrary = {
10071007
if (old.fd === newfd) return -{{{ cDefs.EINVAL }}};
10081008
var existing = FS.getStream(newfd);
10091009
if (existing) FS.close(existing);
1010-
return FS.createStream(old, newfd).fd;
1010+
return FS.dupStream(old, newfd).fd;
10111011
},
10121012
};
10131013

test/fs/test_nodefs_dup.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright 2018 The Emscripten Authors. All rights reserved.
3+
* Emscripten is available under two separate licenses, the MIT license and the
4+
* University of Illinois/NCSA Open Source License. Both these licenses can be
5+
* found in the LICENSE file.
6+
*/
7+
8+
#include <assert.h>
9+
#include <emscripten.h>
10+
#include <fcntl.h>
11+
#include <stdio.h>
12+
#include <unistd.h>
13+
14+
#ifdef NODERAWFS
15+
#define CWD ""
16+
#else
17+
#define CWD "/working/"
18+
#endif
19+
20+
int main(void)
21+
{
22+
EM_ASM(
23+
#ifdef NODERAWFS
24+
FS.close(FS.open('test.txt', 'w'));
25+
#else
26+
FS.mkdir('/working');
27+
FS.mount(NODEFS, {root: '.'}, '/working');
28+
FS.close(FS.open('/working/test.txt', 'w'));
29+
#endif
30+
);
31+
int fd1 = open(CWD "test.txt", O_WRONLY);
32+
int fd2 = dup(fd1);
33+
int fd3 = fcntl(fd1, F_DUPFD_CLOEXEC, 0);
34+
35+
assert(fd1 == 3);
36+
assert(fd2 == 4);
37+
assert(fd3 == 5);
38+
assert(close(fd1) == 0);
39+
assert(write(fd2, "abcdef", 6) == 6);
40+
assert(close(fd2) == 0);
41+
assert(write(fd3, "ghijkl", 6) == 6);
42+
assert(close(fd3) == 0);
43+
printf("success\n");
44+
return 0;
45+
}

test/test_core.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5745,6 +5745,14 @@ def test_fs_nodefs_cloexec(self):
57455745
self.emcc_args += ['-lnodefs.js']
57465746
self.do_runf('fs/test_nodefs_cloexec.c', 'success')
57475747

5748+
@also_with_noderawfs
5749+
@requires_node
5750+
def test_fs_nodefs_dup(self):
5751+
if self.get_setting('WASMFS'):
5752+
self.set_setting('FORCE_FILESYSTEM')
5753+
self.emcc_args += ['-lnodefs.js']
5754+
self.do_runf('fs/test_nodefs_dup.c', 'success')
5755+
57485756
@requires_node
57495757
def test_fs_nodefs_home(self):
57505758
self.set_setting('FORCE_FILESYSTEM')

0 commit comments

Comments
 (0)