Skip to content

Make fstat work on file descriptor with no name #23058

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
f3f36d5
Make fstat work on file descriptor with no name
hoodmane Dec 3, 2024
260278f
Fix node rawfs
hoodmane Dec 3, 2024
f865bdb
Cleanup
hoodmane Dec 3, 2024
cac8b19
Fix nodefs handling of unnamed file descriptors
hoodmane Dec 3, 2024
cad0ddf
Fix indentation
hoodmane Dec 4, 2024
8b1711e
Apply review comments to test
hoodmane Dec 4, 2024
c961354
Fix flake8
hoodmane Dec 4, 2024
bebfc6b
Better argment names and comments
hoodmane Dec 4, 2024
7f4533e
Fix ftruncate
hoodmane Dec 4, 2024
2c41571
Fix directory fds for nameless file patch
hoodmane Dec 4, 2024
6a4dfc2
Implement both truncate and ftruncate via truncateCommon
hoodmane Dec 4, 2024
9d0c79a
Merge branch 'main' into anonymous-file-descriptors
hoodmane Dec 5, 2024
baadd84
Merge branch 'main' into anonymous-file-descriptors
hoodmane Dec 6, 2024
3aa358f
Fix merge
hoodmane Dec 6, 2024
04192f0
Merge branch 'main' into anonymous-file-descriptors
hoodmane Dec 9, 2024
989cf22
Refactor things a bit more
hoodmane Dec 9, 2024
ad02402
Change doStat to writeStat
hoodmane Dec 9, 2024
84bcf30
Fix tests
hoodmane Dec 9, 2024
ba35730
Fix reference error
hoodmane Dec 9, 2024
4e67e7f
Fix test
hoodmane Dec 10, 2024
9399402
Declare stream variable
hoodmane Dec 10, 2024
7593dc1
Merge branch 'main' into anonymous-file-descriptors
hoodmane Dec 12, 2024
efff804
Merge branch 'main' into anonymous-file-descriptors
hoodmane Dec 18, 2024
20de480
Merge branch 'main' into anonymous-file-descriptors
hoodmane Dec 19, 2024
e83ac75
Remove incorrect comment
hoodmane Dec 19, 2024
b509358
Fix merge
hoodmane Dec 19, 2024
8c556c8
Address review comment
hoodmane Dec 19, 2024
4ed8004
Merge branch 'main' into anonymous-file-descriptors
hoodmane Jan 6, 2025
bdfca83
Fix test
hoodmane Jan 6, 2025
4fdc681
Fix test
hoodmane Jan 8, 2025
ddb3fab
Merge branch 'main' into anonymous-file-descriptors
hoodmane Jan 9, 2025
bfd07c7
Fix test
hoodmane Jan 9, 2025
5d4c6d2
Merge branch 'main' into anonymous-file-descriptors
hoodmane Jan 9, 2025
ac100ab
Move stream ops access to streamGetAttr and streamSetAttr
hoodmane Jan 9, 2025
d1f1962
Fix syntax error
hoodmane Jan 9, 2025
02e0b69
Slightly shorter
hoodmane Jan 9, 2025
910b824
Slightly shorter
hoodmane Jan 9, 2025
37f8ee9
Some fixes
hoodmane Jan 9, 2025
5eef321
Fix more
hoodmane Jan 9, 2025
ad6f42a
More fixes
hoodmane Jan 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 48 additions & 17 deletions src/library_fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,24 @@ FS.staticInit();
stream.stream_ops?.dup?.(stream);
return stream;
},
streamGetAttr(stream) {
var node = stream.node;
var res = stream.stream_ops?.getattr?.(stream) ?? node.node_ops?.getattr?.(node);
if (res) {
return res;
}
throw new FS.ErrnoError({{{ cDefs.EPERM }}});
},
streamSetAttr(stream, attr) {
var node = stream.node;
var set;
if (set = stream.stream_ops.setattr) {
return set(stream, attr);
} else if (set = node.node_ops.setattr) {
return set(node, attr);
}
throw new FS.ErrnoError({{{ cDefs.EPERM }}});
},

//
// devices
Expand Down Expand Up @@ -956,6 +974,10 @@ FS.staticInit();
}
return node.node_ops.getattr(node);
},
fstat(fd) {
var stream = FS.getStreamChecked(fd);
return FS.streamGetAttr(stream);
},
lstat(path) {
return FS.stat(path, true);
},
Expand All @@ -981,7 +1003,10 @@ FS.staticInit();
},
fchmod(fd, mode) {
var stream = FS.getStreamChecked(fd);
FS.chmod(stream.node, mode);
FS.streamSetAttr(stream, {
mode: (mode & {{{ cDefs.S_IALLUGO }}}) | (stream.node.mode & ~{{{ cDefs.S_IALLUGO }}}),
ctime: Date.now()
});
},
chown(path, uid, gid, dontFollow) {
var node;
Expand All @@ -1005,7 +1030,22 @@ FS.staticInit();
},
fchown(fd, uid, gid) {
var stream = FS.getStreamChecked(fd);
FS.chown(stream.node, uid, gid);
FS.streamSetAttr(stream, {
timestamp: Date.now()
// we ignore the uid / gid for now
});
},
truncateChecks(node) {
if (FS.isDir(node.mode)) {
throw new FS.ErrnoError({{{ cDefs.EISDIR }}});
}
if (!FS.isFile(node.mode)) {
throw new FS.ErrnoError({{{ cDefs.EINVAL }}});
}
var errCode = FS.nodePermissions(node, 'w');
if (errCode) {
throw new FS.ErrnoError(errCode);
}
},
truncate(path, len) {
if (len < 0) {
Expand All @@ -1018,30 +1058,21 @@ FS.staticInit();
} else {
node = path;
}
if (!node.node_ops.setattr) {
throw new FS.ErrnoError({{{ cDefs.EPERM }}});
}
if (FS.isDir(node.mode)) {
throw new FS.ErrnoError({{{ cDefs.EISDIR }}});
}
if (!FS.isFile(node.mode)) {
throw new FS.ErrnoError({{{ cDefs.EINVAL }}});
}
var errCode = FS.nodePermissions(node, 'w');
if (errCode) {
throw new FS.ErrnoError(errCode);
}
node.node_ops.setattr(node, {
size: len,
timestamp: Date.now()
});
},
ftruncate(fd, len) {
var stream = FS.getStreamChecked(fd);
if ((stream.flags & {{{ cDefs.O_ACCMODE }}}) === {{{ cDefs.O_RDONLY}}}) {
if (len < 0 || (stream.flags & {{{ cDefs.O_ACCMODE }}}) === {{{ cDefs.O_RDONLY}}}) {
throw new FS.ErrnoError({{{ cDefs.EINVAL }}});
}
FS.truncate(stream.node, len);
FS.truncateChecks(stream.node);
FS.streamSetAttr(stream, {
size: len,
timestamp: Date.now()
});
},
utime(path, atime, mtime) {
var lookup = FS.lookupPath(path, { follow: true });
Expand Down
148 changes: 81 additions & 67 deletions src/library_nodefs.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,74 +116,84 @@ addToLibrary({
}
return newFlags;
},

getattr(func, node) {
var stat = NODEFS.tryFSOperation(func);
if (NODEFS.isWindows) {
// node.js v0.10.20 doesn't report blksize and blocks on Windows. Fake
// them with default blksize of 4096.
// See http://support.microsoft.com/kb/140365
if (!stat.blksize) {
stat.blksize = 4096;
}
if (!stat.blocks) {
stat.blocks = (stat.size+stat.blksize-1)/stat.blksize|0;
}
// Windows does not report the 'x' permission bit, so propagate read
// bits to execute bits.
stat.mode |= (stat.mode & {{{ cDefs.S_IRUGO }}}) >> 2;
}
return {
dev: stat.dev,
ino: node.id,
mode: stat.mode,
nlink: stat.nlink,
uid: stat.uid,
gid: stat.gid,
rdev: stat.rdev,
size: stat.size,
atime: stat.atime,
mtime: stat.mtime,
ctime: stat.ctime,
blksize: stat.blksize,
blocks: stat.blocks
};
},
// Common code for both node and stream setattr
// For node getatrr:
// - arg is a native path
// - chmod, utimes, truncate are fs.chmodSync, fs.utimesSync, fs.truncateSync
// For stream getatrr:
// - arg is a native file descriptor
// - chmod, utimes, truncate are fs.fchmodSync, fs.futimesSync, fs.ftruncateSync
setattr(arg, node, attr, chmod, utimes, truncate, stat) {
NODEFS.tryFSOperation(() => {
if (attr.mode !== undefined) {
var mode = attr.mode;
if (NODEFS.isWindows) {
// Windows only supports S_IREAD / S_IWRITE (S_IRUSR / S_IWUSR)
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/chmod-wchmod
mode &= {{{ cDefs.S_IRUSR | cDefs.S_IWUSR }}};
}
chmod(arg, mode);
// update the common node structure mode as well
node.mode = attr.mode;
}
if (typeof (attr.atime ?? attr.mtime) === "number") {
// Unfortunately, we have to stat the current value if we don't want
// to change it. On top of that, since the times don't round trip
// this will only keep the value nearly unchanged not exactly
// unchanged. See:
// https://github.com/nodejs/node/issues/56492
var atime = new Date(attr.atime ?? stat(arg).atime);
var mtime = new Date(attr.mtime ?? stat(arg).mtime);
utimes(arg, atime, mtime);
}
if (attr.size !== undefined) {
truncate(arg, attr.size);
}
});
},
node_ops: {
getattr(node) {
var path = NODEFS.realPath(node);
var stat;
NODEFS.tryFSOperation(() => stat = fs.lstatSync(path));
if (NODEFS.isWindows) {
// node.js v0.10.20 doesn't report blksize and blocks on Windows. Fake
// them with default blksize of 4096.
// See http://support.microsoft.com/kb/140365
if (!stat.blksize) {
stat.blksize = 4096;
}
if (!stat.blocks) {
stat.blocks = (stat.size+stat.blksize-1)/stat.blksize|0;
}
// Windows does not report the 'x' permission bit, so propagate read
// bits to execute bits.
stat.mode |= (stat.mode & {{{ cDefs.S_IRUGO }}}) >> 2;
}
return {
dev: stat.dev,
ino: node.id,
mode: stat.mode,
nlink: stat.nlink,
uid: stat.uid,
gid: stat.gid,
rdev: stat.rdev,
size: stat.size,
atime: stat.atime,
mtime: stat.mtime,
ctime: stat.ctime,
blksize: stat.blksize,
blocks: stat.blocks
};
return NODEFS.getattr(() => fs.lstatSync(path), node);
},
setattr(node, attr) {
var path = NODEFS.realPath(node);
NODEFS.tryFSOperation(() => {
if (attr.mode !== undefined) {
if (attr.dontFollow) {
throw new FS.ErrnoError({{{ cDefs.ENOSYS }}});
}
var mode = attr.mode;
if (NODEFS.isWindows) {
// Windows only supports S_IREAD / S_IWRITE (S_IRUSR / S_IWUSR)
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/chmod-wchmod
mode &= {{{ cDefs.S_IRUSR | cDefs.S_IWUSR }}};
}
fs.chmodSync(path, mode);
// update the common node structure mode as well
node.mode = attr.mode;
}
if (typeof (attr.atime ?? attr.mtime) === "number") {
// Unfortunately, we have to stat the current value if we don't want
// to change it. On top of that, since the times don't round trip
// this will only keep the value nearly unchanged not exactly
// unchanged. See:
// https://github.com/nodejs/node/issues/56492
var stat = () => fs.lstatSync(NODEFS.realPath(node));
var atime = new Date(attr.atime ?? stat().atime);
var mtime = new Date(attr.mtime ?? stat().mtime);
fs.utimesSync(path, atime, mtime);
}
if (attr.size !== undefined) {
fs.truncateSync(path, attr.size);
}
});
if (attr.mode != null && attr.dontFollow) {
throw new FS.ErrnoError({{{ cDefs.ENOSYS }}});
}
NODEFS.setattr(path, node, attr, fs.chmodSync, fs.utimesSync, fs.truncateSync, fs.lstatSync);
},
lookup(parent, name) {
var path = PATH.join2(NODEFS.realPath(parent), name);
Expand Down Expand Up @@ -241,18 +251,22 @@ addToLibrary({
}
},
stream_ops: {
getattr(stream) {
return NODEFS.getattr(() => fs.fstatSync(stream.nfd), stream.node);
},
setattr(stream, attr) {
NODEFS.setattr(stream.nfd, stream.node, attr, fs.fchmodSync, fs.futimesSync, fs.ftruncateSync, fs.fstatSync);
},
open(stream) {
var path = NODEFS.realPath(stream.node);
NODEFS.tryFSOperation(() => {
if (FS.isFile(stream.node.mode)) {
stream.shared.refcount = 1;
stream.nfd = fs.openSync(path, NODEFS.flagsForNode(stream.flags));
}
stream.shared.refcount = 1;
stream.nfd = fs.openSync(path, NODEFS.flagsForNode(stream.flags));
});
},
close(stream) {
NODEFS.tryFSOperation(() => {
if (FS.isFile(stream.node.mode) && stream.nfd && --stream.shared.refcount === 0) {
if (stream.nfd && --stream.shared.refcount === 0) {
fs.closeSync(stream.nfd);
}
});
Expand Down
4 changes: 4 additions & 0 deletions src/library_noderawfs.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ addToLibrary({
}
return stat;
},
fstat(fd) {
var stream = FS.getStreamChecked(fd);
return fs.fstatSync(stream.nfd);
},
chmod(path, mode, dontFollow) {
mode &= {{{ cDefs.S_IALLUGO }}};
if (NODEFS.isWindows) {
Expand Down
3 changes: 1 addition & 2 deletions src/library_syscall.js
Original file line number Diff line number Diff line change
Expand Up @@ -678,8 +678,7 @@ var SyscallsLibrary = {
return SYSCALLS.writeStat(buf, FS.lstat(path));
},
__syscall_fstat64: (fd, buf) => {
var stream = SYSCALLS.getStreamFromFD(fd);
return SYSCALLS.writeStat(buf, FS.stat(stream.path));
return SYSCALLS.writeStat(buf, FS.fstat(fd));
},
__syscall_fchown32: (fd, owner, group) => {
FS.fchown(fd, owner, group);
Expand Down
20 changes: 20 additions & 0 deletions test/fs/test_stat_unnamed_file_descriptor.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <assert.h>
#include "stdio.h"

int main() {
int fd = open("file.txt", O_RDWR | O_CREAT, 0666);
unlink("file.txt");
int res;
struct stat buf;
res = fstat(fd, &buf);
assert(res == 0);
assert(buf.st_atime > 1000000000);
res = fchmod(fd, 0777);
assert(res == 0);
res = ftruncate(fd, 10);
assert(res == 0);
printf("success\n");
}
10 changes: 10 additions & 0 deletions test/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -5847,6 +5847,16 @@ def test_fs_64bit(self):
self.set_setting('FORCE_FILESYSTEM')
self.do_runf('fs/test_64bit.c', 'success')

@crossplatform
@also_with_nodefs_both
def test_fs_stat_unnamed_file_descriptor(self):
nodefs = '-DNODEFS' in self.emcc_args or '-DNODERAWFS' in self.emcc_args
if self.get_setting('WASMFS'):
if nodefs:
self.skipTest('NODEFS in WasmFS')
self.set_setting('FORCE_FILESYSTEM')
self.do_runf('fs/test_stat_unnamed_file_descriptor.c', 'success')

@requires_node
@crossplatform
@with_all_fs
Expand Down
Loading