Skip to content
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

When opening broken symlink with O_CREAT, create file at target #23002

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
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 @@ -6105,6 +6105,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):
hoodmane marked this conversation as resolved.
Show resolved Hide resolved
self.do_run_in_out_file_test('unistd/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
31 changes: 31 additions & 0 deletions test/unistd/test_unistd_write_broken_link.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>


int main() {
int res = symlink("link_target", "link_source");
printf("link result: %d\n", res);
int src_fd = open("link_source", 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("link_target", 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("link_source", 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/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