diff --git a/src/library_fs.js b/src/library_fs.js index b6f0a5092b23d..09ce3763d87f3 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -471,6 +471,11 @@ FS.staticInit();` + closeStream(fd) { FS.streams[fd] = null; }, + dupStream(origStream, fd = -1) { + var stream = FS.createStream(origStream, fd); + stream.stream_ops?.dup?.(stream); + return stream; + }, // // devices diff --git a/src/library_nodefs.js b/src/library_nodefs.js index 81864ffcc956f..ace50458c9c43 100644 --- a/src/library_nodefs.js +++ b/src/library_nodefs.js @@ -251,6 +251,7 @@ addToLibrary({ var path = NODEFS.realPath(stream.node); try { if (FS.isFile(stream.node.mode)) { + stream.shared.refcount = 1; stream.nfd = fs.openSync(path, NODEFS.flagsForNode(stream.flags)); } } catch (e) { @@ -260,7 +261,7 @@ addToLibrary({ }, close(stream) { try { - if (FS.isFile(stream.node.mode) && stream.nfd) { + if (FS.isFile(stream.node.mode) && stream.nfd && --stream.shared.refcount === 0) { fs.closeSync(stream.nfd); } } catch (e) { @@ -268,6 +269,9 @@ addToLibrary({ throw new FS.ErrnoError(NODEFS.convertNodeCode(e)); } }, + dup(stream) { + stream.shared.refcount++; + }, read(stream, buffer, offset, length, position) { // Node.js < 6 compatibility: node errors on 0 length reads if (length === 0) return 0; diff --git a/src/library_syscall.js b/src/library_syscall.js index b078bd71cf7c4..69ea6f8b210b1 100644 --- a/src/library_syscall.js +++ b/src/library_syscall.js @@ -183,7 +183,7 @@ var SyscallsLibrary = { }, __syscall_dup: (fd) => { var old = SYSCALLS.getStreamFromFD(fd); - return FS.createStream(old).fd; + return FS.dupStream(old).fd; }, __syscall_pipe__deps: ['$PIPEFS'], __syscall_pipe: (fdPtr) => { @@ -760,7 +760,7 @@ var SyscallsLibrary = { arg++; } var newStream; - newStream = FS.createStream(stream, arg); + newStream = FS.dupStream(stream, arg); return newStream.fd; } case {{{ cDefs.F_GETFD }}}: @@ -1007,7 +1007,7 @@ var SyscallsLibrary = { if (old.fd === newfd) return -{{{ cDefs.EINVAL }}}; var existing = FS.getStream(newfd); if (existing) FS.close(existing); - return FS.createStream(old, newfd).fd; + return FS.dupStream(old, newfd).fd; }, }; diff --git a/test/fs/test_nodefs_dup.c b/test/fs/test_nodefs_dup.c new file mode 100644 index 0000000000000..abf34935b11c3 --- /dev/null +++ b/test/fs/test_nodefs_dup.c @@ -0,0 +1,45 @@ +/* + * Copyright 2018 The Emscripten Authors. All rights reserved. + * Emscripten is available under two separate licenses, the MIT license and the + * University of Illinois/NCSA Open Source License. Both these licenses can be + * found in the LICENSE file. + */ + +#include +#include +#include +#include +#include + +#ifdef NODERAWFS +#define CWD "" +#else +#define CWD "/working/" +#endif + +int main(void) +{ + EM_ASM( +#ifdef NODERAWFS + FS.close(FS.open('test.txt', 'w')); +#else + FS.mkdir('/working'); + FS.mount(NODEFS, {root: '.'}, '/working'); + FS.close(FS.open('/working/test.txt', 'w')); +#endif + ); + int fd1 = open(CWD "test.txt", O_WRONLY); + int fd2 = dup(fd1); + int fd3 = fcntl(fd1, F_DUPFD_CLOEXEC, 0); + + assert(fd1 == 3); + assert(fd2 == 4); + assert(fd3 == 5); + assert(close(fd1) == 0); + assert(write(fd2, "abcdef", 6) == 6); + assert(close(fd2) == 0); + assert(write(fd3, "ghijkl", 6) == 6); + assert(close(fd3) == 0); + printf("success\n"); + return 0; +} diff --git a/test/test_core.py b/test/test_core.py index 1dd31b8966199..8b67c80ed1a05 100644 --- a/test/test_core.py +++ b/test/test_core.py @@ -5749,6 +5749,14 @@ def test_fs_nodefs_cloexec(self): self.emcc_args += ['-lnodefs.js'] self.do_runf('fs/test_nodefs_cloexec.c', 'success') + @also_with_noderawfs + @requires_node + def test_fs_nodefs_dup(self): + if self.get_setting('WASMFS'): + self.set_setting('FORCE_FILESYSTEM') + self.emcc_args += ['-lnodefs.js'] + self.do_runf('fs/test_nodefs_dup.c', 'success') + @requires_node def test_fs_nodefs_home(self): self.set_setting('FORCE_FILESYSTEM')