Skip to content
29 changes: 20 additions & 9 deletions src/library_fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,18 @@ FS.staticInit();
break;
}

current = FS.lookupNode(current, parts[i]);
current_path = PATH.join2(current_path, parts[i]);
try {
current = FS.lookupNode(current, parts[i]);
} catch (e) {
// if noent_okay is true, suppress a ENOENT in the last component
// and return an object with an undefined node. This is needed for
// resolving symlinks in the path when creating a file.
if ((e?.errno === {{{ cDefs.ENOENT }}}) && islast && opts.noent_okay) {
return { path: current_path };
}
throw e;
}

// jump to the mount's root node if this is a mountpoint
if (FS.isMountpoint(current) && (!islast || opts.follow_mount)) {
Expand Down Expand Up @@ -1034,14 +1044,15 @@ FS.staticInit();
node = path;
} else {
path = PATH.normalize(path);
try {
var lookup = FS.lookupPath(path, {
follow: !(flags & {{{ cDefs.O_NOFOLLOW }}})
});
node = lookup.node;
} catch (e) {
// ignore
}
// noent_okay makes it so that if the final component of the path
// doesn't exist, lookupPath returns `node: undefined`. `path` will be
// updated to point to the target of all symlinks.
var lookup = FS.lookupPath(path, {
follow: !(flags & {{{ cDefs.O_NOFOLLOW }}}),
noent_okay: true
});
node = lookup.node;
path = lookup.path;
}
// perhaps we need to create the node
var created = false;
Expand Down
4 changes: 4 additions & 0 deletions test/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -6095,6 +6095,10 @@ def test_unistd_links(self, args, nodefs):

self.do_run_in_out_file_test('unistd/links.c', emcc_args=args)

@also_with_noderawfs
def test_unistd_write_broken_link(self):
self.do_run_in_out_file_test('unistd/write_broken_link.c')

@no_windows('Skipping NODEFS test, since it would require administrative privileges.')
@requires_node
def test_unistd_symlink_on_nodefs(self):
Expand Down
34 changes: 34 additions & 0 deletions test/unistd/write_broken_link.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>


int main() {
char* p1 = "link_source";
char* p2 = "link_target";

int res = symlink(p2, p1);
printf("link result: %d\n", res);
int src_fd = open(p1, O_CREAT | O_WRONLY, 0777);
printf("source_fd: %d, errno: %d %s\n", src_fd, errno, strerror(errno));
write(src_fd, "abc", 3);
close(src_fd);
{
int target_fd = open(p2, O_RDONLY);
printf("target_fd: %d, errno: %d %s\n", target_fd, errno, strerror(errno));
char buf[10];
read(target_fd, buf, 10);
printf("buf: '%s'\n", buf);
close(target_fd);
}
{
int target_fd = open(p1, O_RDONLY);
printf("target_fd: %d, errno: %d %s\n", target_fd, errno, strerror(errno));
char buf[10];
read(target_fd, buf, 10);
printf("buf: '%s'\n", buf);
close(target_fd);
}
}
6 changes: 6 additions & 0 deletions test/unistd/write_broken_link.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
link result: 0
source_fd: 3, errno: 0 No error information
target_fd: 3, errno: 0 No error information
buf: 'abc'
target_fd: 3, errno: 0 No error information
buf: 'abc'
4 changes: 0 additions & 4 deletions test/wasmfs/wasmfs_open.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,7 @@ int main() {
int fd5 = open("/dev/stdout/foo", O_RDWR);
printf("Errno: %s\n", strerror(errno));
// Both errors are valid, but in WasmFS, ENOTDIR is returned first.
#ifdef WASMFS
assert(errno == ENOTDIR);
#else
assert(errno == ENOENT);
#endif

errno = 0;
// Attempt to open and write to the root directory.
Expand Down
Loading