Skip to content

Commit 874df5b

Browse files
committed
Add basic emulation of getcwd/chdir
This commit adds basic emulation of a current working directory to wasi-libc. The `getcwd` and `chdir` symbols are now implemented and available for use. The `getcwd` implementation is pretty simple in that it just copies out of a new global, `__wasilibc_cwd`, which defaults to `"/"`. The `chdir` implementation is much more involved and has more ramification, however. A new function, `make_absolute`, was added to the preopens object. Paths stored in the preopen table are now always stored as absolute paths instead of relative paths, and initial relative paths are interpreted as being relative to `/`. Looking up a path to preopen now always turns it into an absolute path, relative to the current working directory, and an appropriate path is then returned. The signature of `__wasilibc_find_relpath` has changed as well. It now returns two path components, one for the absolute part and one for the relative part. Additionally the relative part is always dynamically allocated since it may no longer be a substring of the original input path. This has been tested lightly against the Rust standard library so far, but I'm not a regular C developer so there's likely a few things to improve!
1 parent 215adc8 commit 874df5b

7 files changed

Lines changed: 270 additions & 92 deletions

File tree

expected/wasm32-wasi/defined-symbols.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ __uflow
250250
__unlist_locked_file
251251
__uselocale
252252
__utc
253+
__wasilibc_cwd
253254
__wasilibc_ensure_environ
254255
__wasilibc_environ
255256
__wasilibc_environ
@@ -367,6 +368,7 @@ ceill
367368
cexp
368369
cexpf
369370
cexpl
371+
chdir
370372
cimag
371373
cimagf
372374
cimagl
@@ -578,6 +580,7 @@ getc
578580
getc_unlocked
579581
getchar
580582
getchar_unlocked
583+
getcwd
581584
getdate
582585
getdate_err
583586
getdelim

libc-bottom-half/headers/public/wasi/libc-find-relpath.h

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,22 @@ extern "C" {
66
#endif
77

88
/**
9-
* Look up the given path in the preopened directory map. If a suitable
10-
* entry is found, return its directory file descriptor, and store the
11-
* computed relative path in *relative_path.
9+
* Look up the given `path`, relative to the cwd, in the preopened directory
10+
* map. If a suitable entry is found, then the file descriptor for that entry
11+
* is returned. Additionally the absolute path of the directory's file
12+
* descriptor is returned in `abs_prefix` and the relative portion that needs
13+
* to be opened is stored in `relative_path`.
1214
*
13-
* Returns -1 if no directories were suitable.
15+
* Returns -1 if no directories were suitable or if an allocation error
16+
* happens.
17+
*
18+
* On success the `relative_path` points to a malloc'd string, so you'll
19+
* need to call `free` on it. The `abs_prefix` return does not need to be
20+
* free'd.
1421
*/
1522
int __wasilibc_find_relpath(const char *path,
16-
const char **__restrict__ relative_path);
23+
const char **__restrict__ abs_prefix,
24+
char **__restrict__ relative_path);
1725

1826
#ifdef __cplusplus
1927
}

libc-bottom-half/sources/chdir.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#include <errno.h>
2+
#include <fcntl.h>
3+
#include <limits.h>
4+
#include <stdlib.h>
5+
#include <string.h>
6+
#include <sys/stat.h>
7+
#include <unistd.h>
8+
#include <wasi/libc-find-relpath.h>
9+
#include <wasi/libc.h>
10+
11+
char *__wasilibc_cwd = "/";
12+
static int __wasilibc_cwd_mallocd = 0;
13+
14+
int chdir(const char *path)
15+
{
16+
// Find a preopen'd directory as well as a relative path we're anchored
17+
// from which we're changing directories to.
18+
char *relative;
19+
const char *abs;
20+
int parent_fd = __wasilibc_find_relpath(path, &abs, &relative);
21+
if (parent_fd == -1) {
22+
errno = ENOENT;
23+
return -1;
24+
}
25+
26+
// Make sure that this directory we're accessing is indeed a directory.
27+
struct stat dirinfo;
28+
int ret = fstatat(parent_fd, relative, &dirinfo, 0);
29+
if (ret == -1) {
30+
free(relative);
31+
return -1;
32+
}
33+
if (!S_ISDIR(dirinfo.st_mode)) {
34+
free(relative);
35+
errno = ENOTDIR;
36+
return -1;
37+
}
38+
39+
// Copy over our new absolute path into `__wasilibc_cwd`. Only copy over
40+
// the relative portion of the path if it's not equal to `.`
41+
size_t len = strlen(abs);
42+
int copy_relative = strcmp(relative, ".") != 0;
43+
char *new_cwd = malloc(len + (copy_relative ? strlen(relative) : 0));
44+
if (new_cwd == NULL) {
45+
errno = ENOMEM;
46+
return -1;
47+
}
48+
strcpy(new_cwd, abs);
49+
if (copy_relative)
50+
strcpy(new_cwd + strlen(abs), relative);
51+
free(relative);
52+
53+
// And set our new malloc'd buffer into the global cwd, freeing the
54+
// previous one if necessary.
55+
char *prev_cwd = __wasilibc_cwd;
56+
__wasilibc_cwd = new_cwd;
57+
if (__wasilibc_cwd_mallocd)
58+
free(prev_cwd);
59+
__wasilibc_cwd_mallocd = 1;
60+
return 0;
61+
}

libc-bottom-half/sources/getcwd.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#include <unistd.h>
2+
#include <errno.h>
3+
#include <string.h>
4+
5+
extern char *__wasilibc_cwd;
6+
7+
char *getcwd(char *buf, size_t size)
8+
{
9+
if (!buf) {
10+
buf = strdup(__wasilibc_cwd);
11+
if (!buf) {
12+
errno = ENOMEM;
13+
return -1;
14+
}
15+
} else {
16+
size_t len = strlen(__wasilibc_cwd);
17+
if (size < strlen(__wasilibc_cwd) + 1) {
18+
errno = ERANGE;
19+
return 0;
20+
}
21+
strcpy(buf, __wasilibc_cwd);
22+
}
23+
return buf;
24+
}
25+

0 commit comments

Comments
 (0)