Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
30 changes: 24 additions & 6 deletions src/lib/libfs.js
Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,25 @@ FS.staticInit();`;
#endif
return parent.node_ops.symlink(parent, newname, oldpath);
},
link(oldpath, newpath, flags) {
var lookup = FS.lookupPath(newpath, { parent: true });
var parent = lookup.node;
if (!parent) {
throw new FS.ErrnoError({{{ cDefs.ENOENT }}});
}
var newname = PATH.basename(newpath);
var errCode = FS.mayCreate(parent, newname);
if (errCode) {
throw new FS.ErrnoError(errCode);
}
// Hardlinks are only supported by filesystem backends that provide a
// `link` node op (e.g. NODERAWFS backed by the host). NODEFS omits it:
// a host hardlink cannot be confined to the mount root.
if (!parent.node_ops.link) {
throw new FS.ErrnoError({{{ cDefs.EMLINK }}});
}
return parent.node_ops.link(parent, newname, oldpath, flags);
},
rename(old_path, new_path) {
var old_dirname = PATH.dirname(old_path);
var new_dirname = PATH.dirname(new_path);
Expand Down Expand Up @@ -1119,13 +1138,12 @@ FS.staticInit();`;
}
FS.doTruncate(stream, stream.node, len);
},
utime(path, atime, mtime) {
var lookup = FS.lookupPath(path, { follow: true });
var node = lookup.node;
var setattr = FS.checkOpExists(node.node_ops.setattr, {{{ cDefs.EPERM }}});
setattr(node, {
utime(path, atime, mtime, dontFollow) {
var lookup = FS.lookupPath(path, { follow: !dontFollow });
FS.doSetAttr(null, lookup.node, {
atime: atime,
mtime: mtime
mtime: mtime,
dontFollow
});
},
open(path, flags, mode = 0o666) {
Expand Down
6 changes: 5 additions & 1 deletion src/lib/libnodefs.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,11 @@ addToLibrary({
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);
// `dontFollow` (AT_SYMLINK_NOFOLLOW): use lutimes so the symlink's own
// timestamps are set without the host resolving it, which would
// otherwise escape the NODEFS mount root.
var utimes = attr.dontFollow ? fs.lutimesSync : fs.utimesSync;
NODEFS.setattr(path, node, attr, fs.chmodSync, utimes, fs.truncateSync, fs.lstatSync);
},
lookup(parent, name) {
var path = PATH.join2(NODEFS.realPath(parent), name);
Expand Down
24 changes: 21 additions & 3 deletions src/lib/libnoderawfs.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ addToLibrary({
},
mkdir(...args) { fs.mkdirSync(...args); },
symlink(...args) { fs.symlinkSync(...args); },
link(oldpath, newpath, flags) {
// AT_SYMLINK_FOLLOW (0x400): dereference oldpath if it is a symlink,
// since node's link(2) links to the symlink itself by default.
if (flags & 0x400) {
oldpath = fs.realpathSync(oldpath);
}
fs.linkSync(oldpath, newpath);
},
rename(...args) { fs.renameSync(...args); },
rmdir(...args) { fs.rmdirSync(...args); },
readdir(...args) { return ['.', '..'].concat(fs.readdirSync(...args)); },
Expand All @@ -99,6 +107,12 @@ addToLibrary({
},
fstat(fd) {
var stream = FS.getStreamChecked(fd);
// Virtual streams (pipes, sockets) have no backing node fd; defer to their
// own getattr rather than node's fs.fstatSync.
var getattr = stream.stream_ops?.getattr ?? stream.node.node_ops?.getattr;
if (getattr) {
return getattr(stream.stream_ops?.getattr ? stream : stream.node);
}
return fs.fstatSync(stream.nfd);
},
statfs(path) {
Expand Down Expand Up @@ -152,16 +166,20 @@ addToLibrary({
var stream = FS.getStreamChecked(fd);
fs.ftruncateSync(stream.nfd, len);
},
utime(path, atime, mtime) {
utime(path, atime, mtime, dontFollow) {
// null here for atime or mtime means UTIME_OMIT was passed. Since node
// doesn't support this concept we need to first find the existing
// timestamps in order to preserve them.
if ((atime === null) || (mtime === null)) {
var st = fs.statSync(path);
var st = dontFollow ? fs.lstatSync(path) : fs.statSync(path);
atime ||= st.atimeMs;
mtime ||= st.mtimeMs;
}
fs.utimesSync(path, atime/1000, mtime/1000);
if (dontFollow) {
fs.lutimesSync(path, atime/1000, mtime/1000);
} else {
fs.utimesSync(path, atime/1000, mtime/1000);
}
},
open(path, flags, mode) {
flags = FS_modeStringToFlags(flags);
Expand Down
2 changes: 2 additions & 0 deletions src/lib/libsigs.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ sigs = {
__syscall_epoll_ctl__sig: 'iiiip',
__syscall_epoll_pwait__sig: 'iipiipp',
__syscall_faccessat__sig: 'iipii',
__syscall_fadvise64__sig: 'iijji',
__syscall_fallocate__sig: 'iiijj',
__syscall_fchdir__sig: 'ii',
__syscall_fchmod__sig: 'iii',
Expand All @@ -258,6 +259,7 @@ sigs = {
__syscall_getsockname__sig: 'iippiii',
__syscall_getsockopt__sig: 'iiiippi',
__syscall_ioctl__sig: 'iiip',
__syscall_linkat__sig: 'iipipi',
__syscall_listen__sig: 'iiiiiii',
__syscall_lstat64__sig: 'ipp',
__syscall_mkdirat__sig: 'iipi',
Expand Down
28 changes: 24 additions & 4 deletions src/lib/libsyscall.js
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,14 @@ var SyscallsLibrary = {
FS.symlink(target, linkpath);
return 0;
},
__syscall_linkat: (olddirfd, oldpath, newdirfd, newpath, flags) => {
oldpath = SYSCALLS.getStr(oldpath);
newpath = SYSCALLS.getStr(newpath);
oldpath = SYSCALLS.calculateAt(olddirfd, oldpath);
newpath = SYSCALLS.calculateAt(newdirfd, newpath);
FS.link(oldpath, newpath, flags);
return 0;
},
__syscall_readlinkat__deps: ['$lengthBytesUTF8', '$stringToUTF8'],
__syscall_readlinkat: (dirfd, path, buf, bufsize) => {
path = SYSCALLS.getStr(path);
Expand Down Expand Up @@ -1009,10 +1017,8 @@ var SyscallsLibrary = {
},
__syscall_utimensat__deps: ['$readI53FromI64'],
__syscall_utimensat: (dirfd, path, times, flags) => {
var nofollow = flags & {{{ cDefs.AT_SYMLINK_NOFOLLOW }}};
path = SYSCALLS.getStr(path);
#if ASSERTIONS
assert(!flags);
#endif
path = SYSCALLS.calculateAt(dirfd, path, true);
var now = Date.now(), atime, mtime;
if (!times) {
Expand Down Expand Up @@ -1042,7 +1048,7 @@ var SyscallsLibrary = {
// null here means UTIME_OMIT was passed. If both were set to UTIME_OMIT then
// we can skip the call completely.
if ((mtime ?? atime) !== null) {
FS.utime(path, atime, mtime);
FS.utime(path, atime, mtime, nofollow);
}
return 0;
},
Expand All @@ -1055,6 +1061,11 @@ var SyscallsLibrary = {
if (offset < 0 || len < 0) {
return -{{{ cDefs.EINVAL }}}
}
// fallocate is only meaningful on regular files; a pipe/socket is a seek
// error (ESPIPE), matching Linux.
if (!SYSCALLS.getStreamFromFD(fd).seekable) {
return -{{{ cDefs.ESPIPE }}};
}
// We only support mode == 0, which means we can implement fallocate
// in terms of ftruncate.
var oldSize = FS.fstat(fd).size;
Expand All @@ -1064,6 +1075,15 @@ var SyscallsLibrary = {
}
return 0;
},
__syscall_fadvise64__i53abi: true,
__syscall_fadvise64: (fd, offset, len, advice) => {
// Advisory only, so a no-op, but a pipe/socket fd is still a seek error
// (ESPIPE), matching Linux.
if (!SYSCALLS.getStreamFromFD(fd).seekable) {
return -{{{ cDefs.ESPIPE }}};
}
return 0;
},
__syscall_dup3: (fd, newfd, flags) => {
if (fd === newfd) return -{{{ cDefs.EINVAL }}};
if (flags & ~{{{ cDefs.O_CLOEXEC }}}) return -{{{ cDefs.EINVAL }}};
Expand Down
9 changes: 0 additions & 9 deletions system/lib/libc/emscripten_syscall_stubs.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,6 @@ weak pid_t __syscall_getppid() {
return g_ppid;
}

weak int __syscall_linkat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath, int flags) {
return -EMLINK; // no hardlinks for us
}

weak int __syscall_getgroups32(int count, gid_t list[]) {
if (count < 1) {
return -EINVAL;
Expand Down Expand Up @@ -170,11 +166,6 @@ weak int __syscall_madvise(void *addr, size_t length, int advice) {
return 0;
}

weak int __syscall_fadvise64(int fd, off_t offset, off_t length, int advice) {
// this is purely advisory, so we don't warn
return 0;
}

weak int __syscall_mlock(const void *addr, size_t len) {
REPORT(mlock);
return 0;
Expand Down
9 changes: 9 additions & 0 deletions system/lib/wasmfs/syscalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1855,6 +1855,15 @@ int __syscall_fadvise64(int fd, off_t offset, off_t len, int advice) {
return 0;
}

int __syscall_linkat(int olddirfd,
const char* oldpath,
int newdirfd,
const char* newpath,
int flags) {
// Hardlinks are not supported in WASMFS.
return -EMLINK;
}

// epoll is implemented in the legacy (non-WASMFS) JS syscall layer only.
int __syscall_epoll_create1(int flags) { return -ENOSYS; }

Expand Down
32 changes: 26 additions & 6 deletions test/codesize/test_codesize_file_preload.expected.js
Original file line number Diff line number Diff line change
Expand Up @@ -2053,6 +2053,27 @@ var FS = {
}
return parent.node_ops.symlink(parent, newname, oldpath);
},
link(oldpath, newpath, flags) {
var lookup = FS.lookupPath(newpath, {
parent: true
});
var parent = lookup.node;
if (!parent) {
throw new FS.ErrnoError(44);
}
var newname = PATH.basename(newpath);
var errCode = FS.mayCreate(parent, newname);
if (errCode) {
throw new FS.ErrnoError(errCode);
}
// Hardlinks are only supported by filesystem backends that provide a
// `link` node op (e.g. NODERAWFS backed by the host). NODEFS omits it:
// a host hardlink cannot be confined to the mount root.
if (!parent.node_ops.link) {
throw new FS.ErrnoError(34);
}
return parent.node_ops.link(parent, newname, oldpath, flags);
},
rename(old_path, new_path) {
var old_dirname = PATH.dirname(old_path);
var new_dirname = PATH.dirname(new_path);
Expand Down Expand Up @@ -2310,15 +2331,14 @@ var FS = {
}
FS.doTruncate(stream, stream.node, len);
},
utime(path, atime, mtime) {
utime(path, atime, mtime, dontFollow) {
var lookup = FS.lookupPath(path, {
follow: true
follow: !dontFollow
});
var node = lookup.node;
var setattr = FS.checkOpExists(node.node_ops.setattr, 63);
setattr(node, {
FS.doSetAttr(null, lookup.node, {
atime,
mtime
mtime,
dontFollow
});
},
open(path, flags, mode = 438) {
Expand Down
8 changes: 4 additions & 4 deletions test/codesize/test_codesize_hello_O0.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"a.out.js": 23671,
"a.out.js.gz": 8602,
"a.out.js": 23679,
"a.out.js.gz": 8604,
"a.out.nodebug.wasm": 15115,
"a.out.nodebug.wasm.gz": 7464,
"total": 38786,
"total_gz": 16066,
"total": 38794,
"total_gz": 16068,
"sent": [
"fd_write"
],
Expand Down
15 changes: 8 additions & 7 deletions test/codesize/test_codesize_hello_dylink_all.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"a.out.js": 268621,
"a.out.nodebug.wasm": 587978,
"total": 856599,
"a.out.js": 269157,
"a.out.nodebug.wasm": 588047,
"total": 857204,
"sent": [
"IMG_Init",
"IMG_Load",
Expand Down Expand Up @@ -226,6 +226,7 @@
"__syscall_epoll_ctl",
"__syscall_epoll_pwait",
"__syscall_faccessat",
"__syscall_fadvise64",
"__syscall_fallocate",
"__syscall_fchdir",
"__syscall_fchmod",
Expand All @@ -243,6 +244,7 @@
"__syscall_getsockname",
"__syscall_getsockopt",
"__syscall_ioctl",
"__syscall_linkat",
"__syscall_listen",
"__syscall_lstat64",
"__syscall_mkdirat",
Expand Down Expand Up @@ -1751,6 +1753,7 @@
"env.__syscall_epoll_ctl",
"env.__syscall_epoll_pwait",
"env.__syscall_faccessat",
"env.__syscall_fadvise64",
"env.__syscall_fallocate",
"env.__syscall_fchdir",
"env.__syscall_fchmod",
Expand All @@ -1768,6 +1771,7 @@
"env.__syscall_getsockname",
"env.__syscall_getsockopt",
"env.__syscall_ioctl",
"env.__syscall_linkat",
"env.__syscall_listen",
"env.__syscall_lstat64",
"env.__syscall_mkdirat",
Expand Down Expand Up @@ -2205,7 +2209,6 @@
"__subvti3",
"__synccall",
"__syscall_acct",
"__syscall_fadvise64",
"__syscall_getegid32",
"__syscall_geteuid32",
"__syscall_getgid32",
Expand All @@ -2219,7 +2222,6 @@
"__syscall_getrusage",
"__syscall_getsid",
"__syscall_getuid32",
"__syscall_linkat",
"__syscall_madvise",
"__syscall_mincore",
"__syscall_mlock",
Expand Down Expand Up @@ -4090,14 +4092,12 @@
"$__subvti3",
"$__synccall",
"$__syscall_acct",
"$__syscall_fadvise64",
"$__syscall_getgroups32",
"$__syscall_getpid",
"$__syscall_getppid",
"$__syscall_getresuid32",
"$__syscall_getrusage",
"$__syscall_getsid",
"$__syscall_linkat",
"$__syscall_madvise",
"$__syscall_mincore",
"$__syscall_mmap2",
Expand Down Expand Up @@ -4926,6 +4926,7 @@
"$pop_arg_long_double",
"$popen",
"$posix_close",
"$posix_fadvise",
"$posix_fallocate",
"$posix_getdents",
"$posix_openpt",
Expand Down
1 change: 1 addition & 0 deletions test/codesize/test_codesize_minimal_O0.expected.js
Original file line number Diff line number Diff line change
Expand Up @@ -1173,6 +1173,7 @@ missingLibrarySymbols.forEach(missingLibrarySymbol)
'FS_mkdir',
'FS_mkdev',
'FS_symlink',
'FS_link',
'FS_rename',
'FS_rmdir',
'FS_readdir',
Expand Down
8 changes: 4 additions & 4 deletions test/codesize/test_codesize_minimal_O0.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"a.out.js": 18849,
"a.out.js.gz": 6766,
"a.out.js": 18857,
"a.out.js.gz": 6768,
"a.out.nodebug.wasm": 1015,
"a.out.nodebug.wasm.gz": 602,
"total": 19864,
"total_gz": 7368,
"total": 19872,
"total_gz": 7370,
"sent": [],
"imports": [],
"exports": [
Expand Down
Loading