Skip to content
This repository was archived by the owner on Oct 7, 2020. It is now read-only.

Commit 258286c

Browse files
committed
wasi: support resolving symlinks
This commit adds support for resolving symlinks during path resolution. This commit also ports all of the remaining tests from nodejs/node#27850, with the 'poll' test currently disabled.
1 parent 7da2677 commit 258286c

15 files changed

+96
-1
lines changed

deps/uvwasi/src/uvwasi.c

+23-1
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,12 @@ static uvwasi_errno_t uvwasi__resolve_path(const struct uvwasi_fd_wrap_t* fd,
3434
/* TODO(cjihrig): path_len is treated as a size. Need to verify if path_len is
3535
really a string length or a size. Also need to verify if it is null
3636
terminated. */
37-
/* TODO(cjihrig): flags is currently unused. */
37+
uv_fs_t realpath_req;
3838
uvwasi_errno_t err;
3939
char* abs_path;
4040
char* tok;
4141
char* ptr;
42+
int realpath_size;
4243
int abs_size;
4344
int input_is_absolute;
4445
int r;
@@ -96,6 +97,27 @@ static uvwasi_errno_t uvwasi__resolve_path(const struct uvwasi_fd_wrap_t* fd,
9697
ptr += r;
9798
}
9899

100+
if ((flags & UVWASI_LOOKUP_SYMLINK_FOLLOW) == UVWASI_LOOKUP_SYMLINK_FOLLOW) {
101+
r = uv_fs_realpath(NULL, &realpath_req, resolved_path, NULL);
102+
if (r == 0) {
103+
realpath_size = strlen(realpath_req.ptr) + 1;
104+
if (realpath_size > PATH_MAX_BYTES) {
105+
err = UVWASI_ENOBUFS;
106+
uv_fs_req_cleanup(&realpath_req);
107+
goto exit;
108+
}
109+
110+
memcpy(resolved_path, realpath_req.ptr, realpath_size);
111+
} else if (r != UV_ENOENT) {
112+
/* Report errors except ENOENT. */
113+
err = uvwasi__translate_uv_error(r);
114+
uv_fs_req_cleanup(&realpath_req);
115+
goto exit;
116+
}
117+
118+
uv_fs_req_cleanup(&realpath_req);
119+
}
120+
99121
/* Verify that the resolved path is still in the sandbox. */
100122
if (resolved_path != strstr(resolved_path, fd->real_path)) {
101123
err = UVWASI_ENOTCAPABLE;

test/fixtures/outside.txt

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
this file is part of the WASI tests. it exists outside of the sandbox, and
2+
should be inaccessible from the WASI tests.
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../input.txt

test/fixtures/wasi/subdir/loop1

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
./loop2

test/fixtures/wasi/subdir/loop2

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
./loop1

test/fixtures/wasi/subdir/outside.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../outside.txt

test/wasi/c/follow_symlink.c

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#include <assert.h>
2+
#include <stdio.h>
3+
4+
int main() {
5+
FILE* file = fopen("/sandbox/subdir/input_link.txt", "r");
6+
assert(file != NULL);
7+
8+
char c = fgetc(file);
9+
while (c != EOF) {
10+
int wrote = fputc(c, stdout);
11+
assert(wrote != EOF);
12+
c = fgetc(file);
13+
}
14+
}

test/wasi/c/poll.c

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#include <assert.h>
2+
#include <poll.h>
3+
#include <time.h>
4+
#include <unistd.h>
5+
6+
int main(void) {
7+
struct pollfd fds[2];
8+
time_t before, now;
9+
int ret;
10+
11+
fds[0] = (struct pollfd){.fd = 1, .events = POLLOUT, .revents = 0};
12+
fds[1] = (struct pollfd){.fd = 2, .events = POLLOUT, .revents = 0};
13+
14+
ret = poll(fds, 2, -1);
15+
assert(ret == 2);
16+
assert(fds[0].revents == POLLOUT);
17+
assert(fds[1].revents == POLLOUT);
18+
19+
fds[0] = (struct pollfd){.fd = 0, .events = POLLIN, .revents = 0};
20+
time(&before);
21+
ret = poll(fds, 1, 2000);
22+
time(&now);
23+
assert(ret == 0);
24+
assert(now - before >= 2);
25+
26+
sleep(1);
27+
time(&now);
28+
assert(now - before >= 3);
29+
30+
return 0;
31+
}

test/wasi/c/symlink_escape.c

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include <assert.h>
2+
#include <errno.h>
3+
#include <stdio.h>
4+
5+
int main() {
6+
FILE* file = fopen("/sandbox/subdir/outside.txt", "r");
7+
assert(file == NULL);
8+
assert(errno == ENOTCAPABLE);
9+
}

test/wasi/c/symlink_loop.c

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include <assert.h>
2+
#include <errno.h>
3+
#include <stdio.h>
4+
5+
int main() {
6+
FILE* file = fopen("/sandbox/subdir/loop1", "r");
7+
assert(file == NULL);
8+
assert(errno == ELOOP);
9+
}

test/wasi/test-wasi.js

+4
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,12 @@ if (process.argv[2] === 'wasi-child') {
5959
runWASI({ test: 'clock_getres' });
6060
runWASI({ test: 'exitcode', exitCode: 120 });
6161
runWASI({ test: 'fd_prestat_get_refresh' });
62+
runWASI({ test: 'follow_symlink', stdout: 'hello from input.txt\n' });
6263
// runWASI({ test: 'getentropy' });
6364
runWASI({ test: 'getrusage' });
6465
runWASI({ test: 'gettimeofday' });
6566
runWASI({ test: 'notdir' });
67+
// runWASI({ test: 'poll' });
6668
runWASI({ test: 'preopen_populates' });
6769
runWASI({ test: 'read_file', stdout: 'hello from input.txt\n' });
6870
runWASI({
@@ -71,5 +73,7 @@ if (process.argv[2] === 'wasi-child') {
7173
});
7274
runWASI({ test: 'stat' });
7375
runWASI({ test: 'stdin', stdin: 'hello world', stdout: 'hello world' });
76+
runWASI({ test: 'symlink_escape' });
77+
runWASI({ test: 'symlink_loop' });
7478
runWASI({ test: 'write_file' });
7579
}

test/wasi/wasm/follow_symlink.wasm

34.1 KB
Binary file not shown.

test/wasi/wasm/poll.wasm

33.3 KB
Binary file not shown.

test/wasi/wasm/symlink_escape.wasm

32.3 KB
Binary file not shown.

test/wasi/wasm/symlink_loop.wasm

32.2 KB
Binary file not shown.

0 commit comments

Comments
 (0)