Skip to content

Commit cac8b19

Browse files
committed
Fix nodefs handling of unnamed file descriptors
1 parent f865bdb commit cac8b19

File tree

4 files changed

+149
-58
lines changed

4 files changed

+149
-58
lines changed

src/library_fs.js

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -940,7 +940,13 @@ FS.staticInit();
940940
},
941941
fstat(fd) {
942942
var stream = FS.getStreamChecked(fd);
943-
return stream.node.node_ops.getattr(stream.node);
943+
var node = stream.node;
944+
if (stream.stream_ops.getattr) {
945+
return stream.stream_ops.getattr(stream);
946+
} else if (node.node_ops.getattr) {
947+
return node.node_ops.getattr(node);
948+
}
949+
throw new FS.ErrnoError({{{ cDefs.EPERM }}});
944950
},
945951
lstat(path) {
946952
return FS.stat(path, true);
@@ -966,7 +972,17 @@ FS.staticInit();
966972
},
967973
fchmod(fd, mode) {
968974
var stream = FS.getStreamChecked(fd);
969-
FS.chmod(stream.node, mode);
975+
var node = stream.node;
976+
var attrs = {
977+
mode: (mode & {{{ cDefs.S_IALLUGO }}}) | (node.mode & ~{{{ cDefs.S_IALLUGO }}}),
978+
timestamp: Date.now()
979+
};
980+
if (stream.stream_ops.getattr) {
981+
return stream.stream_ops.setattr(stream, attrs);
982+
} else if (node.node_ops.getattr) {
983+
return node.node_ops.setattr(node, attrs);
984+
}
985+
throw new FS.ErrnoError({{{ cDefs.EPERM }}});
970986
},
971987
chown(path, uid, gid, dontFollow) {
972988
var node;
@@ -989,7 +1005,17 @@ FS.staticInit();
9891005
},
9901006
fchown(fd, uid, gid) {
9911007
var stream = FS.getStreamChecked(fd);
992-
FS.chown(stream.node, uid, gid);
1008+
var node = stream.node;
1009+
var attrs = {
1010+
timestamp: Date.now()
1011+
// we ignore the uid / gid for now
1012+
};
1013+
if (stream.stream_ops.getattr) {
1014+
return stream.stream_ops.setattr(stream, attrs);
1015+
} else if (node.node_ops.getattr) {
1016+
return node.node_ops.setattr(node, attrs);
1017+
}
1018+
throw new FS.ErrnoError({{{ cDefs.EPERM }}});
9931019
},
9941020
truncate(path, len) {
9951021
if (len < 0) {
@@ -1022,10 +1048,32 @@ FS.staticInit();
10221048
},
10231049
ftruncate(fd, len) {
10241050
var stream = FS.getStreamChecked(fd);
1051+
var node = stream.node;
10251052
if ((stream.flags & {{{ cDefs.O_ACCMODE }}}) === {{{ cDefs.O_RDONLY}}}) {
10261053
throw new FS.ErrnoError({{{ cDefs.EINVAL }}});
10271054
}
1028-
FS.truncate(stream.node, len);
1055+
if (!node.node_ops.setattr && !stream.stream_ops.setattr) {
1056+
throw new FS.ErrnoError({{{ cDefs.EPERM }}});
1057+
}
1058+
if (FS.isDir(node.mode)) {
1059+
throw new FS.ErrnoError({{{ cDefs.EISDIR }}});
1060+
}
1061+
if (!FS.isFile(node.mode)) {
1062+
throw new FS.ErrnoError({{{ cDefs.EINVAL }}});
1063+
}
1064+
var errCode = FS.nodePermissions(node, 'w');
1065+
if (errCode) {
1066+
throw new FS.ErrnoError(errCode);
1067+
}
1068+
var attrs = {
1069+
size: len,
1070+
timestamp: Date.now()
1071+
};
1072+
if (stream.stream_ops.setattr) {
1073+
stream.stream_ops.setattr(stream, attrs);
1074+
} else {
1075+
node.node_ops.setattr(node, attrs);
1076+
}
10291077
},
10301078
utime(path, atime, mtime) {
10311079
var lookup = FS.lookupPath(path, { follow: true });

src/library_nodefs.js

Lines changed: 62 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -116,64 +116,68 @@ addToLibrary({
116116
}
117117
return newFlags;
118118
},
119-
120-
node_ops: {
121-
getattr(node) {
122-
var path = NODEFS.realPath(node);
123-
var stat;
124-
NODEFS.tryFSOperation(() => stat = fs.lstatSync(path));
119+
getattr(func) {
120+
var stat = NODEFS.tryFSOperation(func);
125121
if (NODEFS.isWindows) {
126-
// node.js v0.10.20 doesn't report blksize and blocks on Windows. Fake
127-
// them with default blksize of 4096.
128-
// See http://support.microsoft.com/kb/140365
129-
if (!stat.blksize) {
130-
stat.blksize = 4096;
131-
}
132-
if (!stat.blocks) {
133-
stat.blocks = (stat.size+stat.blksize-1)/stat.blksize|0;
122+
// node.js v0.10.20 doesn't report blksize and blocks on Windows. Fake
123+
// them with default blksize of 4096.
124+
// See http://support.microsoft.com/kb/140365
125+
if (!stat.blksize) {
126+
stat.blksize = 4096;
127+
}
128+
if (!stat.blocks) {
129+
stat.blocks = (stat.size+stat.blksize-1)/stat.blksize|0;
130+
}
131+
// Windows does not report the 'x' permission bit, so propagate read
132+
// bits to execute bits.
133+
stat.mode |= (stat.mode & {{{ cDefs.S_IRUGO }}}) >> 2;
134+
}
135+
return {
136+
dev: stat.dev,
137+
ino: stat.ino,
138+
mode: stat.mode,
139+
nlink: stat.nlink,
140+
uid: stat.uid,
141+
gid: stat.gid,
142+
rdev: stat.rdev,
143+
size: stat.size,
144+
atime: stat.atime,
145+
mtime: stat.mtime,
146+
ctime: stat.ctime,
147+
blksize: stat.blksize,
148+
blocks: stat.blocks
149+
};
150+
},
151+
setattr(path, node, attr, chmod, utimes, truncate) {
152+
NODEFS.tryFSOperation(() => {
153+
if (attr.mode !== undefined) {
154+
var mode = attr.mode;
155+
if (NODEFS.isWindows) {
156+
// Windows only supports S_IREAD / S_IWRITE (S_IRUSR / S_IWUSR)
157+
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/chmod-wchmod
158+
mode &= {{{ cDefs.S_IRUSR | cDefs.S_IWUSR }}};
134159
}
135-
// Windows does not report the 'x' permission bit, so propagate read
136-
// bits to execute bits.
137-
stat.mode |= (stat.mode & {{{ cDefs.S_IRUGO }}}) >> 2;
160+
chmod(path, mode);
161+
// update the common node structure mode as well
162+
node.mode = attr.mode;
163+
}
164+
if (attr.timestamp !== undefined) {
165+
var date = new Date(attr.timestamp);
166+
utimes(path, date, date);
138167
}
139-
return {
140-
dev: stat.dev,
141-
ino: stat.ino,
142-
mode: stat.mode,
143-
nlink: stat.nlink,
144-
uid: stat.uid,
145-
gid: stat.gid,
146-
rdev: stat.rdev,
147-
size: stat.size,
148-
atime: stat.atime,
149-
mtime: stat.mtime,
150-
ctime: stat.ctime,
151-
blksize: stat.blksize,
152-
blocks: stat.blocks
153-
};
168+
if (attr.size !== undefined) {
169+
truncate(path, attr.size);
170+
}
171+
});
172+
},
173+
node_ops: {
174+
getattr(node) {
175+
var path = NODEFS.realPath(node);
176+
return NODEFS.getattr(() => fs.lstatSync(path));
154177
},
155178
setattr(node, attr) {
156179
var path = NODEFS.realPath(node);
157-
NODEFS.tryFSOperation(() => {
158-
if (attr.mode !== undefined) {
159-
var mode = attr.mode;
160-
if (NODEFS.isWindows) {
161-
// Windows only supports S_IREAD / S_IWRITE (S_IRUSR / S_IWUSR)
162-
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/chmod-wchmod
163-
mode &= {{{ cDefs.S_IRUSR | cDefs.S_IWUSR }}};
164-
}
165-
fs.chmodSync(path, mode);
166-
// update the common node structure mode as well
167-
node.mode = attr.mode;
168-
}
169-
if (attr.timestamp !== undefined) {
170-
var date = new Date(attr.timestamp);
171-
fs.utimesSync(path, date, date);
172-
}
173-
if (attr.size !== undefined) {
174-
fs.truncateSync(path, attr.size);
175-
}
176-
});
180+
NODEFS.setattr(path, node, attr, fs.chmodSync, fs.utimesSync, fs.truncateSync);
177181
},
178182
lookup(parent, name) {
179183
var path = PATH.join2(NODEFS.realPath(parent), name);
@@ -228,6 +232,12 @@ addToLibrary({
228232
}
229233
},
230234
stream_ops: {
235+
getattr(stream) {
236+
return NODEFS.getattr(() => fs.fstatSync(stream.nfd));
237+
},
238+
setattr(stream, attr) {
239+
NODEFS.setattr(stream.nfd, stream.node, attr, fs.fchmodSync, fs.futimesSync, fs.ftruncateSync);
240+
},
231241
open(stream) {
232242
var path = NODEFS.realPath(stream.node);
233243
NODEFS.tryFSOperation(() => {

test/fs/test_stat_unnamed_file_descriptor.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,37 @@
44
#include <assert.h>
55
#include "stdio.h"
66

7+
#ifdef __EMSCRIPTEN__
8+
#include <emscripten.h>
9+
#endif
10+
11+
void makedir(const char *dir) {
12+
int rtn = mkdir(dir, 0777);
13+
assert(rtn == 0);
14+
}
15+
16+
void changedir(const char *dir) {
17+
int rtn = chdir(dir);
18+
assert(rtn == 0);
19+
}
20+
21+
722
int main() {
23+
makedir("working");
24+
#if defined(__EMSCRIPTEN__) && defined(NODEFS)
25+
EM_ASM(FS.mount(NODEFS, { root: '.' }, 'working'));
26+
#endif
27+
changedir("working");
828
int fd = open("file.txt", O_RDWR | O_CREAT, 0666);
929
unlink("file.txt");
1030
int res;
1131
struct stat buf;
1232
res = fstat(fd, &buf);
1333
assert(res == 0);
1434
assert(buf.st_atime > 1000000000);
35+
res = fchmod(fd, 0777);
36+
assert(res == 0);
37+
res = ftruncate(fd, 10);
38+
assert(res == 0);
1539
printf("success\n");
1640
}

test/test_core.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5921,9 +5921,18 @@ def test_fs_64bit(self):
59215921
self.set_setting('FORCE_FILESYSTEM')
59225922
self.do_runf('fs/test_64bit.c', 'success')
59235923

5924-
@also_with_noderawfs
5925-
def test_fs_stat_unnamed_file_descriptor(self):
5924+
@requires_node
5925+
@parameterized({
5926+
'': ([],),
5927+
'nodefs': (['-DNODEFS', '-lnodefs.js'],),
5928+
'noderawfs':(['-sNODERAWFS'],),
5929+
})
5930+
def test_fs_stat_unnamed_file_descriptor(self, args):
5931+
self.emcc_args += args
5932+
nodefs = '-DNODEFS' in args or '-DNODERAWFS' in args
59265933
if self.get_setting('WASMFS'):
5934+
if nodefs:
5935+
self.skipTest('NODEFS in WasmFS')
59275936
self.set_setting('FORCE_FILESYSTEM')
59285937
self.do_runf('fs/test_stat_unnamed_file_descriptor.c', 'success')
59295938

0 commit comments

Comments
 (0)