Skip to content

Commit 355cdff

Browse files
socketpairjakogut
authored andcommittedNov 27, 2019
Fix dup*() syscalls
Rework dup syscalls so file descriptors share all flags and also seek position.
1 parent e5ccdbc commit 355cdff

File tree

6 files changed

+110
-23
lines changed

6 files changed

+110
-23
lines changed
 

‎src/library_fs.js

+38-8
Original file line numberDiff line numberDiff line change
@@ -400,10 +400,10 @@ mergeInto(LibraryManager.library, {
400400
getStream: function(fd) {
401401
return FS.streams[fd];
402402
},
403-
// TODO parameterize this function such that a stream
404-
// object isn't directly passed in. not possible until
405-
// SOCKFS is completed.
406-
createStream: function(stream, fd_start, fd_end) {
403+
duplicateStream: function(stream) {
404+
// Some properties are shared between duplicates
405+
var sharedProperties = ["position", "flags", "refcount"];
406+
407407
if (!FS.FSStream) {
408408
FS.FSStream = function(){};
409409
FS.FSStream.prototype = {};
@@ -421,15 +421,45 @@ mergeInto(LibraryManager.library, {
421421
},
422422
isAppend: {
423423
get: function() { return (this.flags & {{{ cDefine('O_APPEND') }}}); }
424-
}
424+
},
425+
flags: {
426+
get: function() { return this.shared.flags; },
427+
set: function(value) { this.shared.flags = value; }
428+
},
429+
position: {
430+
get: function() { return this.shared.position; },
431+
set: function(value) { this.shared.position = value; }
432+
},
433+
refcount: {
434+
get: function() { return this.shared.refcount; },
435+
set: function(value) { this.shared.refcount = value; }
436+
}
425437
});
426438
}
427-
// clone it, so we can return an instance of FSStream
439+
428440
var newStream = new FS.FSStream();
429441
for (var p in stream) {
430-
newStream[p] = stream[p];
442+
if (sharedProperties.indexOf(p) >= 0) {
443+
if (!("shared" in newStream))
444+
newStream.shared = {};
445+
newStream.shared[p] = stream[p];
446+
} else {
447+
newStream[p] = stream[p];
448+
}
449+
}
450+
451+
if ("refcount" in newStream.shared) {
452+
newStream.refcount += 1;
431453
}
432-
stream = newStream;
454+
455+
return newStream;
456+
},
457+
// TODO parameterize this function such that a stream
458+
// object isn't directly passed in. not possible until
459+
// SOCKFS is completed.
460+
createStream: function(stream, fd_start, fd_end) {
461+
// clone it, so we can return an instance of FSStream
462+
stream = FS.duplicateStream(stream);
433463
var fd = FS.nextfd(fd_start, fd_end);
434464
stream.fd = fd;
435465
FS.streams[fd] = stream;

‎src/library_noderawfs.js

+14-3
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,28 @@ mergeInto(LibraryManager.library, {
5151
if (typeof flags === "string") {
5252
flags = VFS.modeStringToFlags(flags)
5353
}
54+
5455
var nfd = fs.openSync(path, NODEFS.flagsForNode(flags), mode);
5556
var fd = suggestFD != null ? suggestFD : FS.nextfd(nfd);
56-
var stream = { fd: fd, nfd: nfd, position: 0, path: path, flags: flags, seekable: true };
57-
FS.streams[fd] = stream;
57+
var stream = FS.createStream({
58+
fd: fd,
59+
nfd: nfd,
60+
refcount: 1,
61+
position: 0,
62+
path: path,
63+
flags: flags,
64+
seekable: true,
65+
}, fd, fd);
5866
return stream;
5967
},
6068
close: function(stream) {
61-
if (!stream.stream_ops) {
69+
stream.shared.refcount -= 1;
70+
71+
if (!stream.stream_ops && !stream.shared.refcount) {
6272
// this stream is created by in-memory filesystem
6373
fs.closeSync(stream.nfd);
6474
}
75+
6576
FS.closeStream(stream.fd);
6677
},
6778
llseek: function(stream, offset, whence) {

‎src/library_syscall.js

+19-9
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,15 @@ var SyscallsLibrary = {
128128
}
129129
return 0;
130130
},
131-
doDup: function(path, flags, suggestFD) {
131+
doDup: function(stream, suggestFD) {
132132
var suggest = FS.getStream(suggestFD);
133133
if (suggest) FS.close(suggest);
134-
return FS.open(path, flags, 0, suggestFD, suggestFD).fd;
134+
135+
var newStream = FS.duplicateStream(stream);
136+
newStream.fd = suggestFD;
137+
138+
FS.streams[suggestFD] = newStream;
139+
return suggestFD;
135140
},
136141
doReadv: function(stream, iov, iovcnt, offset) {
137142
var ret = 0;
@@ -366,7 +371,7 @@ var SyscallsLibrary = {
366371
},
367372
__syscall41: function(which, varargs) { // dup
368373
var old = SYSCALLS.getStreamFromFD();
369-
return FS.open(old.path, old.flags, 0).fd;
374+
return SYSCALLS.doDup(old, FS.nextfd());
370375
},
371376
__syscall42__deps: ['$PIPEFS'],
372377
__syscall42: function(which, varargs) { // pipe
@@ -459,7 +464,7 @@ var SyscallsLibrary = {
459464
__syscall63: function(which, varargs) { // dup2
460465
var old = SYSCALLS.getStreamFromFD(), suggestFD = SYSCALLS.get();
461466
if (old.fd === suggestFD) return suggestFD;
462-
return SYSCALLS.doDup(old.path, old.flags, suggestFD);
467+
return SYSCALLS.doDup(old, suggestFD);
463468
},
464469
__syscall64__deps: ['$PROCINFO'],
465470
__syscall64: function(which, varargs) { // getppid
@@ -1074,14 +1079,14 @@ var SyscallsLibrary = {
10741079
#else
10751080
var stream = SYSCALLS.getStreamFromFD(), cmd = SYSCALLS.get();
10761081
switch (cmd) {
1082+
// TODO: FreeBSD also defines F_DUP2FD and F_DUP2FD_CLOEXEC
1083+
case {{{ cDefine('F_DUPFD_CLOEXEC') }}}: // ignore O_CLOEXEC in single process environment
10771084
case {{{ cDefine('F_DUPFD') }}}: {
10781085
var arg = SYSCALLS.get();
10791086
if (arg < 0) {
10801087
return -{{{ cDefine('EINVAL') }}};
10811088
}
1082-
var newStream;
1083-
newStream = FS.open(stream.path, stream.flags, 0, arg);
1084-
return newStream.fd;
1089+
return SYSCALLS.doDup(stream, FS.nextfd(arg));
10851090
}
10861091
case {{{ cDefine('F_GETFD') }}}:
10871092
case {{{ cDefine('F_SETFD') }}}:
@@ -1308,8 +1313,13 @@ var SyscallsLibrary = {
13081313
#if ASSERTIONS
13091314
assert(!flags);
13101315
#endif
1311-
if (old.fd === suggestFD) return -{{{ cDefine('EINVAL') }}};
1312-
return SYSCALLS.doDup(old.path, old.flags, suggestFD);
1316+
if (old.fd === suggestFD) return -ERRNO_CODES.EINVAL;
1317+
1318+
// intentionally ignore flags, since O_CLOEXEC make no sense in
1319+
// singleprocess environment
1320+
if (flags & ~{{{ cDefine('O_CLOEXEC') }}}) return -ERRNO_CODES.EINVAL;
1321+
1322+
return SYSCALLS.doDup(old, suggestFD);
13131323
},
13141324
__syscall331: function(which, varargs) { // pipe2
13151325
return -{{{ cDefine('ENOSYS') }}}; // unsupported feature

‎src/struct_info.json

+2
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,11 @@
129129
"F_SETLK64",
130130
"F_GETLK",
131131
"S_ISVTX",
132+
"O_CLOEXEC",
132133
"O_RDONLY",
133134
"O_ACCMODE",
134135
"F_DUPFD",
136+
"F_DUPFD_CLOEXEC",
135137
"F_SETLK",
136138
"O_WRONLY",
137139
"AT_FDCWD",

‎tests/unistd/dup.c

+29-3
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,41 @@
1212

1313
int main() {
1414
int f, f2, f3;
15+
off_t offset;
16+
17+
char *fnam = "/tmp/emscripten_test_file";
18+
remove(fnam);
19+
errno = 0;
20+
21+
f = open(fnam, O_RDWR | O_CREAT, S_IRUSR | S_IRGRP | S_IROTH);
22+
close(f);
1523

1624
printf("DUP\n");
17-
f = open("/", O_RDONLY);
18-
f2 = open("/", O_RDONLY);
25+
f = open(fnam, O_RDONLY);
26+
f2 = open(fnam, O_RDONLY);
1927
f3 = dup(f);
2028
printf("errno: %d\n", errno);
2129
printf("f: %d\n", f != f2 && f != f3);
2230
printf("f2,f3: %d\n", f2 != f3);
31+
32+
// dup()ed file descriptors should share all flags and even seek position
33+
offset = lseek(f3, 0, SEEK_CUR);
34+
printf("1. f3 offset was %d. Should be 0\n", (int)offset);
35+
offset = lseek(f, 1, SEEK_SET);
36+
printf("2. f offset set to %d. Should be 1\n", (int)offset);
37+
offset = lseek(f2, 2, SEEK_SET);
38+
printf("3. f2 offset set to %d. Should be 2\n", (int)offset);
39+
offset = lseek(f, 0, SEEK_CUR);
40+
printf("4. f offset now is %d. Should be 1\n", (int)offset);
41+
offset = lseek(f2, 0, SEEK_CUR);
42+
printf("5. f2 offset now is %d. Should be 2\n", (int)offset);
43+
offset = lseek(f3, 0, SEEK_CUR);
44+
printf("6. f3 offset now is %d. Should be 1 (and not 0)\n", (int)offset);
45+
offset = lseek(f3, 3, SEEK_SET);
46+
printf("7. f3 offset set to %d. Should be 3\n", (int)offset);
47+
offset = lseek(f, 0, SEEK_CUR);
48+
printf("8. f offset now is %d. Should be 3 (and not 1)\n", (int)offset);
49+
2350
printf("close(f1): %d\n", close(f));
2451
printf("close(f2): %d\n", close(f2));
2552
printf("close(f3): %d\n", close(f3));
@@ -36,7 +63,6 @@ int main() {
3663
printf("close(f1): %d\n", close(f));
3764
printf("close(f2): %d\n", close(f2));
3865
printf("close(f3): %d\n", close(f3));
39-
printf("\n");
4066
errno = 0;
4167

4268
return 0;

‎tests/unistd/dup.out

+8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@ DUP
22
errno: 0
33
f: 1
44
f2,f3: 1
5+
1. f3 offset was 0. Should be 0
6+
2. f offset set to 1. Should be 1
7+
3. f2 offset set to 2. Should be 2
8+
4. f offset now is 1. Should be 1
9+
5. f2 offset now is 2. Should be 2
10+
6. f3 offset now is 1. Should be 1 (and not 0)
11+
7. f3 offset set to 3. Should be 3
12+
8. f offset now is 3. Should be 3 (and not 1)
513
close(f1): 0
614
close(f2): 0
715
close(f3): 0

0 commit comments

Comments
 (0)
Failed to load comments.