From 874df5be4dc05a8099d078f551f3957ec2e3c9b8 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 4 Aug 2020 14:14:36 -0700 Subject: [PATCH 01/11] 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! --- expected/wasm32-wasi/defined-symbols.txt | 3 + .../headers/public/wasi/libc-find-relpath.h | 18 +- libc-bottom-half/sources/chdir.c | 61 ++++++ libc-bottom-half/sources/getcwd.c | 25 +++ libc-bottom-half/sources/posix.c | 175 +++++++++++------- libc-bottom-half/sources/preopens.c | 74 +++++--- libc-top-half/musl/include/unistd.h | 6 +- 7 files changed, 270 insertions(+), 92 deletions(-) create mode 100644 libc-bottom-half/sources/chdir.c create mode 100644 libc-bottom-half/sources/getcwd.c diff --git a/expected/wasm32-wasi/defined-symbols.txt b/expected/wasm32-wasi/defined-symbols.txt index 7cab06cb8..79e34ff06 100644 --- a/expected/wasm32-wasi/defined-symbols.txt +++ b/expected/wasm32-wasi/defined-symbols.txt @@ -250,6 +250,7 @@ __uflow __unlist_locked_file __uselocale __utc +__wasilibc_cwd __wasilibc_ensure_environ __wasilibc_environ __wasilibc_environ @@ -367,6 +368,7 @@ ceill cexp cexpf cexpl +chdir cimag cimagf cimagl @@ -578,6 +580,7 @@ getc getc_unlocked getchar getchar_unlocked +getcwd getdate getdate_err getdelim diff --git a/libc-bottom-half/headers/public/wasi/libc-find-relpath.h b/libc-bottom-half/headers/public/wasi/libc-find-relpath.h index 7395ad6da..58d5c8ba0 100644 --- a/libc-bottom-half/headers/public/wasi/libc-find-relpath.h +++ b/libc-bottom-half/headers/public/wasi/libc-find-relpath.h @@ -6,14 +6,22 @@ extern "C" { #endif /** - * Look up the given path in the preopened directory map. If a suitable - * entry is found, return its directory file descriptor, and store the - * computed relative path in *relative_path. + * Look up the given `path`, relative to the cwd, in the preopened directory + * map. If a suitable entry is found, then the file descriptor for that entry + * is returned. Additionally the absolute path of the directory's file + * descriptor is returned in `abs_prefix` and the relative portion that needs + * to be opened is stored in `relative_path`. * - * Returns -1 if no directories were suitable. + * Returns -1 if no directories were suitable or if an allocation error + * happens. + * + * On success the `relative_path` points to a malloc'd string, so you'll + * need to call `free` on it. The `abs_prefix` return does not need to be + * free'd. */ int __wasilibc_find_relpath(const char *path, - const char **__restrict__ relative_path); + const char **__restrict__ abs_prefix, + char **__restrict__ relative_path); #ifdef __cplusplus } diff --git a/libc-bottom-half/sources/chdir.c b/libc-bottom-half/sources/chdir.c new file mode 100644 index 000000000..a398e634f --- /dev/null +++ b/libc-bottom-half/sources/chdir.c @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char *__wasilibc_cwd = "/"; +static int __wasilibc_cwd_mallocd = 0; + +int chdir(const char *path) +{ + // Find a preopen'd directory as well as a relative path we're anchored + // from which we're changing directories to. + char *relative; + const char *abs; + int parent_fd = __wasilibc_find_relpath(path, &abs, &relative); + if (parent_fd == -1) { + errno = ENOENT; + return -1; + } + + // Make sure that this directory we're accessing is indeed a directory. + struct stat dirinfo; + int ret = fstatat(parent_fd, relative, &dirinfo, 0); + if (ret == -1) { + free(relative); + return -1; + } + if (!S_ISDIR(dirinfo.st_mode)) { + free(relative); + errno = ENOTDIR; + return -1; + } + + // Copy over our new absolute path into `__wasilibc_cwd`. Only copy over + // the relative portion of the path if it's not equal to `.` + size_t len = strlen(abs); + int copy_relative = strcmp(relative, ".") != 0; + char *new_cwd = malloc(len + (copy_relative ? strlen(relative) : 0)); + if (new_cwd == NULL) { + errno = ENOMEM; + return -1; + } + strcpy(new_cwd, abs); + if (copy_relative) + strcpy(new_cwd + strlen(abs), relative); + free(relative); + + // And set our new malloc'd buffer into the global cwd, freeing the + // previous one if necessary. + char *prev_cwd = __wasilibc_cwd; + __wasilibc_cwd = new_cwd; + if (__wasilibc_cwd_mallocd) + free(prev_cwd); + __wasilibc_cwd_mallocd = 1; + return 0; +} diff --git a/libc-bottom-half/sources/getcwd.c b/libc-bottom-half/sources/getcwd.c new file mode 100644 index 000000000..78208721a --- /dev/null +++ b/libc-bottom-half/sources/getcwd.c @@ -0,0 +1,25 @@ +#include +#include +#include + +extern char *__wasilibc_cwd; + +char *getcwd(char *buf, size_t size) +{ + if (!buf) { + buf = strdup(__wasilibc_cwd); + if (!buf) { + errno = ENOMEM; + return -1; + } + } else { + size_t len = strlen(__wasilibc_cwd); + if (size < strlen(__wasilibc_cwd) + 1) { + errno = ERANGE; + return 0; + } + strcpy(buf, __wasilibc_cwd); + } + return buf; +} + diff --git a/libc-bottom-half/sources/posix.c b/libc-bottom-half/sources/posix.c index 64e6bfaa9..b9c1d20a5 100644 --- a/libc-bottom-half/sources/posix.c +++ b/libc-bottom-half/sources/posix.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -18,8 +19,9 @@ int open(const char *path, int oflag, ...) { // See the documentation in libc.h int __wasilibc_open_nomode(const char *path, int oflag) { - const char *relative_path; - int dirfd = __wasilibc_find_relpath(path, &relative_path); + const char *abs; + char *relative_path; + int dirfd = __wasilibc_find_relpath(path, &abs, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. if (dirfd == -1) { @@ -27,12 +29,15 @@ int __wasilibc_open_nomode(const char *path, int oflag) { return -1; } - return __wasilibc_openat_nomode(dirfd, relative_path, oflag); + int ret = __wasilibc_openat_nomode(dirfd, relative_path, oflag); + free(relative_path); + return ret; } int access(const char *path, int amode) { - const char *relative_path; - int dirfd = __wasilibc_find_relpath(path, &relative_path); + const char *abs; + char *relative_path; + int dirfd = __wasilibc_find_relpath(path, &abs, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. if (dirfd == -1) { @@ -40,7 +45,9 @@ int access(const char *path, int amode) { return -1; } - return faccessat(dirfd, relative_path, amode, 0); + int ret = faccessat(dirfd, relative_path, amode, 0); + free(relative_path); + return ret; } ssize_t readlink( @@ -48,8 +55,9 @@ ssize_t readlink( char *restrict buf, size_t bufsize) { - const char *relative_path; - int dirfd = __wasilibc_find_relpath(path, &relative_path); + const char *abs; + char *relative_path; + int dirfd = __wasilibc_find_relpath(path, &abs, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. if (dirfd == -1) { @@ -57,12 +65,15 @@ ssize_t readlink( return -1; } - return readlinkat(dirfd, relative_path, buf, bufsize); + ssize_t ret = readlinkat(dirfd, relative_path, buf, bufsize); + free(relative_path); + return ret; } int stat(const char *restrict path, struct stat *restrict buf) { - const char *relative_path; - int dirfd = __wasilibc_find_relpath(path, &relative_path); + const char *abs; + char *relative_path; + int dirfd = __wasilibc_find_relpath(path, &abs, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. if (dirfd == -1) { @@ -70,12 +81,15 @@ int stat(const char *restrict path, struct stat *restrict buf) { return -1; } - return fstatat(dirfd, relative_path, buf, 0); + int ret = fstatat(dirfd, relative_path, buf, 0); + free(relative_path); + return ret; } int lstat(const char *restrict path, struct stat *restrict buf) { - const char *relative_path; - int dirfd = __wasilibc_find_relpath(path, &relative_path); + const char *abs; + char *relative_path; + int dirfd = __wasilibc_find_relpath(path, &abs, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. if (dirfd == -1) { @@ -83,12 +97,15 @@ int lstat(const char *restrict path, struct stat *restrict buf) { return -1; } - return fstatat(dirfd, relative_path, buf, AT_SYMLINK_NOFOLLOW); + int ret = fstatat(dirfd, relative_path, buf, AT_SYMLINK_NOFOLLOW); + free(relative_path); + return ret; } int utime(const char *path, const struct utimbuf *times) { - const char *relative_path; - int dirfd = __wasilibc_find_relpath(path, &relative_path); + const char *abs; + char *relative_path; + int dirfd = __wasilibc_find_relpath(path, &abs, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. if (dirfd == -1) { @@ -96,18 +113,21 @@ int utime(const char *path, const struct utimbuf *times) { return -1; } - return utimensat(dirfd, relative_path, + int ret = utimensat(dirfd, relative_path, times ? ((struct timespec [2]) { { .tv_sec = times->actime }, { .tv_sec = times->modtime } }) : NULL, 0); + free(relative_path); + return ret; } int unlink(const char *path) { - const char *relative_path; - int dirfd = __wasilibc_find_relpath(path, &relative_path); + const char *abs; + char *relative_path; + int dirfd = __wasilibc_find_relpath(path, &abs, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. if (dirfd == -1) { @@ -118,12 +138,15 @@ int unlink(const char *path) { // `unlinkat` imports `__wasi_path_remove_directory` even when // `AT_REMOVEDIR` isn't passed. Instead, use a specialized function which // just imports `__wasi_path_unlink_file`. - return __wasilibc_unlinkat(dirfd, relative_path); + int ret = __wasilibc_unlinkat(dirfd, relative_path); + free(relative_path); + return ret; } int rmdir(const char *path) { - const char *relative_path; - int dirfd = __wasilibc_find_relpath(path, &relative_path); + const char *abs; + char *relative_path; + int dirfd = __wasilibc_find_relpath(path, &abs, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. if (dirfd == -1) { @@ -131,12 +154,15 @@ int rmdir(const char *path) { return -1; } - return __wasilibc_rmdirat(dirfd, relative_path); + int ret = __wasilibc_rmdirat(dirfd, relative_path); + free(relative_path); + return ret; } int remove(const char *path) { - const char *relative_path; - int dirfd = __wasilibc_find_relpath(path, &relative_path); + const char *abs; + char *relative_path; + int dirfd = __wasilibc_find_relpath(path, &abs, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. if (dirfd == -1) { @@ -154,12 +180,14 @@ int remove(const char *path) { if (errno == ENOTDIR) errno = ENOTCAPABLE; } + free(relative_path); return r; } int mkdir(const char *path, mode_t mode) { - const char *relative_path; - int dirfd = __wasilibc_find_relpath(path, &relative_path); + const char *abs; + char *relative_path; + int dirfd = __wasilibc_find_relpath(path, &abs, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. if (dirfd == -1) { @@ -167,12 +195,15 @@ int mkdir(const char *path, mode_t mode) { return -1; } - return mkdirat(dirfd, relative_path, mode); + int ret = mkdirat(dirfd, relative_path, mode); + free(relative_path); + return ret; } DIR *opendir(const char *dirname) { - const char *relative_path; - int dirfd = __wasilibc_find_relpath(dirname, &relative_path); + const char *abs; + char *relative_path; + int dirfd = __wasilibc_find_relpath(dirname, &abs, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. if (dirfd == -1) { @@ -180,7 +211,9 @@ DIR *opendir(const char *dirname) { return NULL; } - return opendirat(dirfd, relative_path); + DIR *ret = opendirat(dirfd, relative_path); + free(relative_path); + return ret; } int scandir( @@ -189,8 +222,9 @@ int scandir( int (*filter)(const struct dirent *), int (*compar)(const struct dirent **, const struct dirent **) ) { - const char *relative_path; - int dirfd = __wasilibc_find_relpath(dir, &relative_path); + const char *abs; + char *relative_path; + int dirfd = __wasilibc_find_relpath(dir, &abs, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. if (dirfd == -1) { @@ -198,12 +232,15 @@ int scandir( return -1; } - return scandirat(dirfd, relative_path, namelist, filter, compar); + int ret = scandirat(dirfd, relative_path, namelist, filter, compar); + free(relative_path); + return ret; } int symlink(const char *target, const char *linkpath) { - const char *relative_path; - int dirfd = __wasilibc_find_relpath(linkpath, &relative_path); + const char *abs; + char *relative_path; + int dirfd = __wasilibc_find_relpath(linkpath, &abs, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. if (dirfd == -1) { @@ -211,41 +248,55 @@ int symlink(const char *target, const char *linkpath) { return -1; } - return symlinkat(target, dirfd, relative_path); + int ret = symlinkat(target, dirfd, relative_path); + free(relative_path); + return ret; } int link(const char *old, const char *new) { - const char *old_relative_path; - int old_dirfd = __wasilibc_find_relpath(old, &old_relative_path); + const char *abs; + char *old_relative_path; + char *new_relative_path; - if (old_dirfd != -1) { - const char *new_relative_path; - int new_dirfd = __wasilibc_find_relpath(new, &new_relative_path); - - if (new_dirfd != -1) - return linkat(old_dirfd, old_relative_path, - new_dirfd, new_relative_path, 0); + int old_dirfd = __wasilibc_find_relpath(old, &abs, &old_relative_path); + // We couldn't find a preopen for it; indicate that we lack capabilities. + if (old_dirfd == -1) { + errno = ENOTCAPABLE; + return -1; + } + int new_dirfd = __wasilibc_find_relpath(new, &abs, &new_relative_path); + if (new_dirfd == -1) { + free(old_relative_path); + errno = ENOTCAPABLE; + return -1; } - // We couldn't find a preopen for it; indicate that we lack capabilities. - errno = ENOTCAPABLE; - return -1; + int ret = linkat(old_dirfd, old_relative_path, new_dirfd, new_relative_path, 0); + free(old_relative_path); + free(new_relative_path); + return ret; } int rename(const char *old, const char *new) { - const char *old_relative_path; - int old_dirfd = __wasilibc_find_relpath(old, &old_relative_path); + const char *abs; + char *old_relative_path; + char *new_relative_path; - if (old_dirfd != -1) { - const char *new_relative_path; - int new_dirfd = __wasilibc_find_relpath(new, &new_relative_path); - - if (new_dirfd != -1) - return renameat(old_dirfd, old_relative_path, - new_dirfd, new_relative_path); + int old_dirfd = __wasilibc_find_relpath(old, &abs, &old_relative_path); + // We couldn't find a preopen for it; indicate that we lack capabilities. + if (old_dirfd == -1) { + errno = ENOTCAPABLE; + return -1; + } + int new_dirfd = __wasilibc_find_relpath(new, &abs, &new_relative_path); + if (new_dirfd == -1) { + free(old_relative_path); + errno = ENOTCAPABLE; + return -1; } - // We couldn't find a preopen for it; indicate that we lack capabilities. - errno = ENOTCAPABLE; - return -1; + int ret = renameat(old_dirfd, old_relative_path, new_dirfd, new_relative_path); + free(old_relative_path); + free(new_relative_path); + return ret; } diff --git a/libc-bottom-half/sources/preopens.c b/libc-bottom-half/sources/preopens.c index fa0e12f99..5e375b808 100644 --- a/libc-bottom-half/sources/preopens.c +++ b/libc-bottom-half/sources/preopens.c @@ -71,15 +71,52 @@ static int resize(void) { return 0; } +extern char *__wasilibc_cwd; + +static char *make_absolute(const char *path) { + // If this path is absolute, then we return it as-is. + if (path[0] == '/') { + return strdup(path); + } + + // If the path is empty, or points to the current directory, then return + // the current directory. + if (path[0] == 0 || !strcmp(path, ".") || !strcmp(path, "./")) { + return strdup(__wasilibc_cwd); + } + + // If the path starts with `./` then we won't be appending that to the cwd. + if (path[0] == '.' && path[1] == '/') + path += 2; + + // Otherwise we'll take the current directory, add a `/`, and then add the + // input `path`. Note that this doesn't do any normalization (like removing + // `/./`). + size_t cwd_len = strlen(__wasilibc_cwd); + size_t path_len = strlen(path); + int need_slash = __wasilibc_cwd[cwd_len - 1] == '/' ? 0 : 1; + char *abspath = malloc(cwd_len + path_len + 1 + need_slash); + if (!abspath) + return abspath; + strcpy(abspath, __wasilibc_cwd); + if (need_slash) + strcpy(abspath + cwd_len, "/"); + strcpy(abspath + cwd_len + need_slash, path); + return abspath; +} + /// Register the given preopened file descriptor under the given path. /// /// This function takes ownership of `prefix`. -static int internal_register_preopened_fd(__wasi_fd_t fd, const char *prefix) { +static int internal_register_preopened_fd(__wasi_fd_t fd, const char *relprefix) { assert_invariants(); if (num_preopens == preopen_capacity && resize() != 0) return -1; + char *prefix = make_absolute(relprefix); + if (prefix == NULL) + return -1; preopens[num_preopens++] = (preopen) { prefix, fd, }; assert_invariants(); @@ -109,41 +146,29 @@ static bool prefix_matches(const char *prefix, size_t prefix_len, const char *pa // See the documentation in libc.h int __wasilibc_register_preopened_fd(int fd, const char *prefix) { - prefix = strdup(prefix); return prefix == NULL ? -1 : internal_register_preopened_fd((__wasi_fd_t)fd, prefix); } // See the documentation in libc-find-relpath.h. -int __wasilibc_find_relpath(const char *path, - const char **restrict relative_path) { +int __wasilibc_find_relpath(const char *relpath, + const char **restrict abs_prefix, + char **restrict relative_path) { assert_invariants(); + char *path = make_absolute(relpath); + if (!path) + return -1; // Search through the preopens table. Iterate in reverse so that more // recently added preopens take precedence over less recently addded ones. size_t match_len = 0; int fd = -1; + *abs_prefix = ""; for (size_t i = num_preopens; i > 0; --i) { const preopen *pre = &preopens[i - 1]; const char *prefix = pre->prefix; size_t len = strlen(prefix); - if (path[0] != '/' && - (path[0] != '.' || (path[1] != '/' && path[1] != '\0'))) - { - // We're matching a relative path that doesn't start with "./" and - // isn't ".". - if (len >= 2 && prefix[0] == '.' && prefix[1] == '/') { - // The preopen starts with "./", so skip that prefix. - prefix += 2; - len -= 2; - } else if (len == 1 && prefix[0] == '.') { - // The preopen is ".", so match it as an empty string. - prefix += 1; - len -= 1; - } - } - // If we haven't had a match yet, or the candidate path is longer than // our current best match's path, and the candidate path is a prefix of // the requested path, take that as the new best path. @@ -152,11 +177,12 @@ int __wasilibc_find_relpath(const char *path, { fd = pre->fd; match_len = len; + *abs_prefix = prefix; } } // The relative path is the substring after the portion that was matched. - const char *computed = path + match_len; + char *computed = path + match_len; // Omit leading slashes in the relative path. while (*computed == '/') @@ -166,7 +192,10 @@ int __wasilibc_find_relpath(const char *path, if (*computed == '\0') computed = "."; - *relative_path = computed; + *relative_path = strdup(computed); + free(path); + if (*relative_path == NULL) + return -1; return fd; } @@ -202,6 +231,7 @@ static void __wasilibc_populate_preopens(void) { if (internal_register_preopened_fd(fd, prefix) != 0) goto software; + free(prefix); break; } diff --git a/libc-top-half/musl/include/unistd.h b/libc-top-half/musl/include/unistd.h index e55f638e9..0eac1cfe9 100644 --- a/libc-top-half/musl/include/unistd.h +++ b/libc-top-half/musl/include/unistd.h @@ -118,11 +118,11 @@ int ftruncate(int, off_t); int access(const char *, int); int faccessat(int, const char *, int, int); -#ifdef __wasilibc_unmodified_upstream /* WASI has no cwd */ -int chdir(const char *); +#ifdef __wasilibc_unmodified_upstream /* WASI has no fchdir */ int fchdir(int); -char *getcwd(char *, size_t); #endif +int chdir(const char *); +char *getcwd(char *, size_t); #ifdef __wasilibc_unmodified_upstream /* WASI has no signals */ unsigned alarm(unsigned); From 2c1c2804b39bf26a288a22a550a631ff649854ba Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 5 Aug 2020 08:38:03 -0700 Subject: [PATCH 02/11] Amortize mallocs made in syscalls --- .../headers/public/wasi/libc-find-relpath.h | 15 +- libc-bottom-half/sources/chdir.c | 95 ++++---- libc-bottom-half/sources/getcwd.c | 28 +-- libc-bottom-half/sources/posix.c | 215 +++++++++--------- libc-bottom-half/sources/preopens.c | 60 +++-- 5 files changed, 215 insertions(+), 198 deletions(-) diff --git a/libc-bottom-half/headers/public/wasi/libc-find-relpath.h b/libc-bottom-half/headers/public/wasi/libc-find-relpath.h index 58d5c8ba0..dd70867be 100644 --- a/libc-bottom-half/headers/public/wasi/libc-find-relpath.h +++ b/libc-bottom-half/headers/public/wasi/libc-find-relpath.h @@ -10,18 +10,19 @@ extern "C" { * map. If a suitable entry is found, then the file descriptor for that entry * is returned. Additionally the absolute path of the directory's file * descriptor is returned in `abs_prefix` and the relative portion that needs - * to be opened is stored in `relative_path`. + * to be opened is stored in `relative_path`. The size of the `relative_path` + * buffer is provided in the `relative_path_len` argument. * - * Returns -1 if no directories were suitable or if an allocation error - * happens. + * Returns -1 on failure. Errno is set to either: * - * On success the `relative_path` points to a malloc'd string, so you'll - * need to call `free` on it. The `abs_prefix` return does not need to be - * free'd. + * * ENOMEM - failed to allocate memory for internal routines. + * * ENOENT - the `path` could not be found relative to any preopened dir. + * * ERANGE - the `relative_path` buffer is too small to hold the relative path. */ int __wasilibc_find_relpath(const char *path, const char **__restrict__ abs_prefix, - char **__restrict__ relative_path); + char *relative_path, + size_t relative_path_len); #ifdef __cplusplus } diff --git a/libc-bottom-half/sources/chdir.c b/libc-bottom-half/sources/chdir.c index a398e634f..4af62c0be 100644 --- a/libc-bottom-half/sources/chdir.c +++ b/libc-bottom-half/sources/chdir.c @@ -8,54 +8,63 @@ #include #include -char *__wasilibc_cwd = "/"; +extern char *__wasilibc_cwd; static int __wasilibc_cwd_mallocd = 0; int chdir(const char *path) { - // Find a preopen'd directory as well as a relative path we're anchored - // from which we're changing directories to. - char *relative; - const char *abs; - int parent_fd = __wasilibc_find_relpath(path, &abs, &relative); - if (parent_fd == -1) { - errno = ENOENT; - return -1; - } + static char *relative_buf = NULL; + static size_t relative_buf_len = 0; - // Make sure that this directory we're accessing is indeed a directory. - struct stat dirinfo; - int ret = fstatat(parent_fd, relative, &dirinfo, 0); - if (ret == -1) { - free(relative); - return -1; - } - if (!S_ISDIR(dirinfo.st_mode)) { - free(relative); - errno = ENOTDIR; - return -1; - } + // Find a preopen'd directory as well as a relative path we're anchored + // from which we're changing directories to. + const char *abs; + int parent_fd; + while (1) { + parent_fd = __wasilibc_find_relpath(path, &abs, relative_buf, relative_buf_len); + if (parent_fd != -1) + break; + if (errno != ERANGE) + return -1; + size_t new_len = relative_buf_len == 0 ? 16 : 2 * relative_buf_len; + relative_buf = realloc(relative_buf, new_len); + if (relative_buf == NULL) { + errno = ENOMEM; + return -1; + } + relative_buf_len = new_len; + } - // Copy over our new absolute path into `__wasilibc_cwd`. Only copy over - // the relative portion of the path if it's not equal to `.` - size_t len = strlen(abs); - int copy_relative = strcmp(relative, ".") != 0; - char *new_cwd = malloc(len + (copy_relative ? strlen(relative) : 0)); - if (new_cwd == NULL) { - errno = ENOMEM; - return -1; - } - strcpy(new_cwd, abs); - if (copy_relative) - strcpy(new_cwd + strlen(abs), relative); - free(relative); + // Make sure that this directory we're accessing is indeed a directory. + struct stat dirinfo; + int ret = fstatat(parent_fd, relative_buf, &dirinfo, 0); + if (ret == -1) { + return -1; + } + if (!S_ISDIR(dirinfo.st_mode)) { + errno = ENOTDIR; + return -1; + } - // And set our new malloc'd buffer into the global cwd, freeing the - // previous one if necessary. - char *prev_cwd = __wasilibc_cwd; - __wasilibc_cwd = new_cwd; - if (__wasilibc_cwd_mallocd) - free(prev_cwd); - __wasilibc_cwd_mallocd = 1; - return 0; + // Copy over our new absolute path into `__wasilibc_cwd`. Only copy over + // the relative portion of the path if it's not equal to `.` + size_t len = strlen(abs); + int copy_relative = strcmp(relative_buf, ".") != 0; + char *new_cwd = malloc(len + (copy_relative ? strlen(relative_buf) : 0)); + if (new_cwd == NULL) { + errno = ENOMEM; + return -1; + } + strcpy(new_cwd, abs); + if (copy_relative) + strcpy(new_cwd + strlen(abs), relative_buf); + + // And set our new malloc'd buffer into the global cwd, freeing the + // previous one if necessary. + char *prev_cwd = __wasilibc_cwd; + __wasilibc_cwd = new_cwd; + if (__wasilibc_cwd_mallocd) + free(prev_cwd); + __wasilibc_cwd_mallocd = 1; + return 0; } diff --git a/libc-bottom-half/sources/getcwd.c b/libc-bottom-half/sources/getcwd.c index 78208721a..e572f3063 100644 --- a/libc-bottom-half/sources/getcwd.c +++ b/libc-bottom-half/sources/getcwd.c @@ -2,24 +2,24 @@ #include #include -extern char *__wasilibc_cwd; +char *__wasilibc_cwd = "/"; char *getcwd(char *buf, size_t size) { - if (!buf) { - buf = strdup(__wasilibc_cwd); if (!buf) { - errno = ENOMEM; - return -1; + buf = strdup(__wasilibc_cwd); + if (!buf) { + errno = ENOMEM; + return NULL; + } + } else { + size_t len = strlen(__wasilibc_cwd); + if (size < strlen(__wasilibc_cwd) + 1) { + errno = ERANGE; + return NULL; + } + strcpy(buf, __wasilibc_cwd); } - } else { - size_t len = strlen(__wasilibc_cwd); - if (size < strlen(__wasilibc_cwd) + 1) { - errno = ERANGE; - return 0; - } - strcpy(buf, __wasilibc_cwd); - } - return buf; + return buf; } diff --git a/libc-bottom-half/sources/posix.c b/libc-bottom-half/sources/posix.c index b9c1d20a5..89bfd2192 100644 --- a/libc-bottom-half/sources/posix.c +++ b/libc-bottom-half/sources/posix.c @@ -5,12 +5,48 @@ #include #include #include +#include #include #include -#include #include #include +static int find_relpath2( + const char *path, + const char **relative, + char **relative_buf, + size_t *relative_buf_len +) { + const char *abs; + + while (1) { + *relative = *relative_buf; + int dirfd = __wasilibc_find_relpath(path, &abs, *relative_buf, *relative_buf_len); + if (dirfd != -1) + return dirfd; + if (errno != ERANGE) + return -1; + size_t new_len = *relative_buf_len == 0 ? 16 : 2 * *relative_buf_len; + char *tmp = realloc(*relative_buf, new_len); + if (tmp == NULL) { + errno = ENOMEM; + return -1; + } + *relative_buf = tmp; + *relative_buf_len = new_len; + } +} + +// Helper to call `__wasilibc_find_relpath` and return an already-managed +// pointer for the `relative` path. This function is not reentrant since the +// `relative` pointer will point to static data that cannot be reused until +// `relative` is no longer used. +static int find_relpath(const char *path, const char **relative) { + static char *relative_buf = NULL; + static size_t relative_buf_len = 0; + return find_relpath2(path, relative, &relative_buf, &relative_buf_len); +} + int open(const char *path, int oflag, ...) { // WASI libc's `openat` ignores the mode argument, so call a special // entrypoint which avoids the varargs calling convention. @@ -19,9 +55,8 @@ int open(const char *path, int oflag, ...) { // See the documentation in libc.h int __wasilibc_open_nomode(const char *path, int oflag) { - const char *abs; - char *relative_path; - int dirfd = __wasilibc_find_relpath(path, &abs, &relative_path); + const char *relative_path; + int dirfd = find_relpath(path, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. if (dirfd == -1) { @@ -29,15 +64,12 @@ int __wasilibc_open_nomode(const char *path, int oflag) { return -1; } - int ret = __wasilibc_openat_nomode(dirfd, relative_path, oflag); - free(relative_path); - return ret; + return __wasilibc_openat_nomode(dirfd, relative_path, oflag); } int access(const char *path, int amode) { - const char *abs; - char *relative_path; - int dirfd = __wasilibc_find_relpath(path, &abs, &relative_path); + const char *relative_path; + int dirfd = find_relpath(path, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. if (dirfd == -1) { @@ -45,9 +77,7 @@ int access(const char *path, int amode) { return -1; } - int ret = faccessat(dirfd, relative_path, amode, 0); - free(relative_path); - return ret; + return faccessat(dirfd, relative_path, amode, 0); } ssize_t readlink( @@ -55,9 +85,8 @@ ssize_t readlink( char *restrict buf, size_t bufsize) { - const char *abs; - char *relative_path; - int dirfd = __wasilibc_find_relpath(path, &abs, &relative_path); + const char *relative_path; + int dirfd = find_relpath(path, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. if (dirfd == -1) { @@ -65,15 +94,12 @@ ssize_t readlink( return -1; } - ssize_t ret = readlinkat(dirfd, relative_path, buf, bufsize); - free(relative_path); - return ret; + return readlinkat(dirfd, relative_path, buf, bufsize); } int stat(const char *restrict path, struct stat *restrict buf) { - const char *abs; - char *relative_path; - int dirfd = __wasilibc_find_relpath(path, &abs, &relative_path); + const char *relative_path; + int dirfd = find_relpath(path, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. if (dirfd == -1) { @@ -81,15 +107,12 @@ int stat(const char *restrict path, struct stat *restrict buf) { return -1; } - int ret = fstatat(dirfd, relative_path, buf, 0); - free(relative_path); - return ret; + return fstatat(dirfd, relative_path, buf, 0); } int lstat(const char *restrict path, struct stat *restrict buf) { - const char *abs; - char *relative_path; - int dirfd = __wasilibc_find_relpath(path, &abs, &relative_path); + const char *relative_path; + int dirfd = find_relpath(path, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. if (dirfd == -1) { @@ -97,15 +120,12 @@ int lstat(const char *restrict path, struct stat *restrict buf) { return -1; } - int ret = fstatat(dirfd, relative_path, buf, AT_SYMLINK_NOFOLLOW); - free(relative_path); - return ret; + return fstatat(dirfd, relative_path, buf, AT_SYMLINK_NOFOLLOW); } int utime(const char *path, const struct utimbuf *times) { - const char *abs; - char *relative_path; - int dirfd = __wasilibc_find_relpath(path, &abs, &relative_path); + const char *relative_path; + int dirfd = find_relpath(path, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. if (dirfd == -1) { @@ -113,21 +133,18 @@ int utime(const char *path, const struct utimbuf *times) { return -1; } - int ret = utimensat(dirfd, relative_path, + return utimensat(dirfd, relative_path, times ? ((struct timespec [2]) { { .tv_sec = times->actime }, { .tv_sec = times->modtime } }) : NULL, 0); - free(relative_path); - return ret; } int unlink(const char *path) { - const char *abs; - char *relative_path; - int dirfd = __wasilibc_find_relpath(path, &abs, &relative_path); + const char *relative_path; + int dirfd = find_relpath(path, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. if (dirfd == -1) { @@ -138,15 +155,12 @@ int unlink(const char *path) { // `unlinkat` imports `__wasi_path_remove_directory` even when // `AT_REMOVEDIR` isn't passed. Instead, use a specialized function which // just imports `__wasi_path_unlink_file`. - int ret = __wasilibc_unlinkat(dirfd, relative_path); - free(relative_path); - return ret; + return __wasilibc_unlinkat(dirfd, relative_path); } int rmdir(const char *path) { - const char *abs; - char *relative_path; - int dirfd = __wasilibc_find_relpath(path, &abs, &relative_path); + const char *relative_path; + int dirfd = find_relpath(path, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. if (dirfd == -1) { @@ -154,15 +168,12 @@ int rmdir(const char *path) { return -1; } - int ret = __wasilibc_rmdirat(dirfd, relative_path); - free(relative_path); - return ret; + return __wasilibc_rmdirat(dirfd, relative_path); } int remove(const char *path) { - const char *abs; - char *relative_path; - int dirfd = __wasilibc_find_relpath(path, &abs, &relative_path); + const char *relative_path; + int dirfd = find_relpath(path, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. if (dirfd == -1) { @@ -180,14 +191,12 @@ int remove(const char *path) { if (errno == ENOTDIR) errno = ENOTCAPABLE; } - free(relative_path); return r; } int mkdir(const char *path, mode_t mode) { - const char *abs; - char *relative_path; - int dirfd = __wasilibc_find_relpath(path, &abs, &relative_path); + const char *relative_path; + int dirfd = find_relpath(path, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. if (dirfd == -1) { @@ -195,15 +204,12 @@ int mkdir(const char *path, mode_t mode) { return -1; } - int ret = mkdirat(dirfd, relative_path, mode); - free(relative_path); - return ret; + return mkdirat(dirfd, relative_path, mode); } DIR *opendir(const char *dirname) { - const char *abs; - char *relative_path; - int dirfd = __wasilibc_find_relpath(dirname, &abs, &relative_path); + const char *relative_path; + int dirfd = find_relpath(dirname, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. if (dirfd == -1) { @@ -211,9 +217,7 @@ DIR *opendir(const char *dirname) { return NULL; } - DIR *ret = opendirat(dirfd, relative_path); - free(relative_path); - return ret; + return opendirat(dirfd, relative_path); } int scandir( @@ -222,9 +226,8 @@ int scandir( int (*filter)(const struct dirent *), int (*compar)(const struct dirent **, const struct dirent **) ) { - const char *abs; - char *relative_path; - int dirfd = __wasilibc_find_relpath(dir, &abs, &relative_path); + const char *relative_path; + int dirfd = find_relpath(dir, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. if (dirfd == -1) { @@ -232,15 +235,12 @@ int scandir( return -1; } - int ret = scandirat(dirfd, relative_path, namelist, filter, compar); - free(relative_path); - return ret; + return scandirat(dirfd, relative_path, namelist, filter, compar); } int symlink(const char *target, const char *linkpath) { - const char *abs; - char *relative_path; - int dirfd = __wasilibc_find_relpath(linkpath, &abs, &relative_path); + const char *relative_path; + int dirfd = find_relpath(linkpath, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. if (dirfd == -1) { @@ -248,55 +248,44 @@ int symlink(const char *target, const char *linkpath) { return -1; } - int ret = symlinkat(target, dirfd, relative_path); - free(relative_path); - return ret; + return symlinkat(target, dirfd, relative_path); } +static char *relative_buf2 = NULL; +static size_t relative_buf_len2 = NULL; + int link(const char *old, const char *new) { - const char *abs; - char *old_relative_path; - char *new_relative_path; + const char *old_relative_path; + int old_dirfd = find_relpath2(old, &old_relative_path, &relative_buf2, &relative_buf_len2); - int old_dirfd = __wasilibc_find_relpath(old, &abs, &old_relative_path); - // We couldn't find a preopen for it; indicate that we lack capabilities. - if (old_dirfd == -1) { - errno = ENOTCAPABLE; - return -1; - } - int new_dirfd = __wasilibc_find_relpath(new, &abs, &new_relative_path); - if (new_dirfd == -1) { - free(old_relative_path); - errno = ENOTCAPABLE; - return -1; + if (old_dirfd != -1) { + const char *new_relative_path; + int new_dirfd = find_relpath(new, &new_relative_path); + + if (new_dirfd != -1) + return linkat(old_dirfd, old_relative_path, + new_dirfd, new_relative_path, 0); } - int ret = linkat(old_dirfd, old_relative_path, new_dirfd, new_relative_path, 0); - free(old_relative_path); - free(new_relative_path); - return ret; + // We couldn't find a preopen for it; indicate that we lack capabilities. + errno = ENOTCAPABLE; + return -1; } int rename(const char *old, const char *new) { - const char *abs; - char *old_relative_path; - char *new_relative_path; + const char *old_relative_path; + int old_dirfd = find_relpath2(old, &old_relative_path, &relative_buf2, &relative_buf_len2); - int old_dirfd = __wasilibc_find_relpath(old, &abs, &old_relative_path); - // We couldn't find a preopen for it; indicate that we lack capabilities. - if (old_dirfd == -1) { - errno = ENOTCAPABLE; - return -1; - } - int new_dirfd = __wasilibc_find_relpath(new, &abs, &new_relative_path); - if (new_dirfd == -1) { - free(old_relative_path); - errno = ENOTCAPABLE; - return -1; + if (old_dirfd != -1) { + const char *new_relative_path; + int new_dirfd = find_relpath(new, &new_relative_path); + + if (new_dirfd != -1) + return renameat(old_dirfd, old_relative_path, + new_dirfd, new_relative_path); } - int ret = renameat(old_dirfd, old_relative_path, new_dirfd, new_relative_path); - free(old_relative_path); - free(new_relative_path); - return ret; + // We couldn't find a preopen for it; indicate that we lack capabilities. + errno = ENOTCAPABLE; + return -1; } diff --git a/libc-bottom-half/sources/preopens.c b/libc-bottom-half/sources/preopens.c index 5e375b808..0c1fd4718 100644 --- a/libc-bottom-half/sources/preopens.c +++ b/libc-bottom-half/sources/preopens.c @@ -7,14 +7,15 @@ #endif #include +#include #include #include #include #include #include #include -#include #include +#include /// A name and file descriptor pair. typedef struct preopen { @@ -73,16 +74,21 @@ static int resize(void) { extern char *__wasilibc_cwd; -static char *make_absolute(const char *path) { +// Note that this function is not reentrant, the returned pointer can only be +// used until the next call of this function. +static const char *make_absolute(const char *path) { + static char *make_absolute_buf = NULL; + static size_t make_absolute_len = 0; + // If this path is absolute, then we return it as-is. if (path[0] == '/') { - return strdup(path); + return path; } // If the path is empty, or points to the current directory, then return // the current directory. if (path[0] == 0 || !strcmp(path, ".") || !strcmp(path, "./")) { - return strdup(__wasilibc_cwd); + return __wasilibc_cwd; } // If the path starts with `./` then we won't be appending that to the cwd. @@ -95,14 +101,18 @@ static char *make_absolute(const char *path) { size_t cwd_len = strlen(__wasilibc_cwd); size_t path_len = strlen(path); int need_slash = __wasilibc_cwd[cwd_len - 1] == '/' ? 0 : 1; - char *abspath = malloc(cwd_len + path_len + 1 + need_slash); - if (!abspath) - return abspath; - strcpy(abspath, __wasilibc_cwd); + size_t alloc_len = cwd_len + path_len + 1 + need_slash; + if (alloc_len > make_absolute_len) { + make_absolute_buf = realloc(make_absolute_buf, alloc_len); + if (make_absolute_buf == NULL) + return NULL; + make_absolute_len = alloc_len; + } + strcpy(make_absolute_buf, __wasilibc_cwd); if (need_slash) - strcpy(abspath + cwd_len, "/"); - strcpy(abspath + cwd_len + need_slash, path); - return abspath; + strcpy(make_absolute_buf + cwd_len, "/"); + strcpy(make_absolute_buf + cwd_len + need_slash, path); + return make_absolute_buf; } /// Register the given preopened file descriptor under the given path. @@ -114,7 +124,7 @@ static int internal_register_preopened_fd(__wasi_fd_t fd, const char *relprefix) if (num_preopens == preopen_capacity && resize() != 0) return -1; - char *prefix = make_absolute(relprefix); + char *prefix = strdup(make_absolute(relprefix)); if (prefix == NULL) return -1; preopens[num_preopens++] = (preopen) { prefix, fd, }; @@ -146,18 +156,20 @@ static bool prefix_matches(const char *prefix, size_t prefix_len, const char *pa // See the documentation in libc.h int __wasilibc_register_preopened_fd(int fd, const char *prefix) { - return prefix == NULL ? -1 : - internal_register_preopened_fd((__wasi_fd_t)fd, prefix); + return internal_register_preopened_fd((__wasi_fd_t)fd, prefix); } // See the documentation in libc-find-relpath.h. int __wasilibc_find_relpath(const char *relpath, const char **restrict abs_prefix, - char **restrict relative_path) { + char *relative_path, + size_t relative_path_len) { assert_invariants(); - char *path = make_absolute(relpath); - if (!path) + const char *path = make_absolute(relpath); + if (!path) { + errno = ENOMEM; return -1; + } // Search through the preopens table. Iterate in reverse so that more // recently added preopens take precedence over less recently addded ones. @@ -181,8 +193,13 @@ int __wasilibc_find_relpath(const char *relpath, } } + if (fd == -1) { + errno = ENOENT; + return -1; + } + // The relative path is the substring after the portion that was matched. - char *computed = path + match_len; + const char *computed = path + match_len; // Omit leading slashes in the relative path. while (*computed == '/') @@ -192,10 +209,11 @@ int __wasilibc_find_relpath(const char *relpath, if (*computed == '\0') computed = "."; - *relative_path = strdup(computed); - free(path); - if (*relative_path == NULL) + if (strlen(computed) > relative_path_len) { + errno = ERANGE; return -1; + } + strcpy(relative_path, computed); return fd; } From 8c47a2504d323940c73a5f29a2dd96af0d04883b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 6 Aug 2020 13:03:48 -0700 Subject: [PATCH 03/11] Avoid size bloat on programs that don't use `chdir` --- Makefile | 8 ++ expected/wasm32-wasi/defined-symbols.txt | 2 + .../headers/public/wasi/libc-find-relpath.h | 26 +++- libc-bottom-half/sources/chdir.c | 113 ++++++++++++++---- libc-bottom-half/sources/posix.c | 84 +++++++------ libc-bottom-half/sources/preopens.c | 94 ++++++--------- 6 files changed, 205 insertions(+), 122 deletions(-) diff --git a/Makefile b/Makefile index a1d0a315b..0a1c76457 100644 --- a/Makefile +++ b/Makefile @@ -44,6 +44,14 @@ LIBC_BOTTOM_HALF_SOURCES = $(LIBC_BOTTOM_HALF_DIR)/sources LIBC_BOTTOM_HALF_ALL_SOURCES = \ $(shell find $(LIBC_BOTTOM_HALF_CLOUDLIBC_SRC) -name \*.c) \ $(shell find $(LIBC_BOTTOM_HALF_SOURCES) -name \*.c) + +# Move `chdir.c` to the end of libc.a since the symbols it provides are used to +# satisfy weak references elsewhere in libc.a, and we don't want to eagerly +# satisfy those references unless `chdir` is pulled in. Some more discussion of +# this can be found on #214 +LIBC_BOTTOM_HALF_ALL_SOURCES := $(filter-out $(LIBC_BOTTOM_HALF_SOURCES)/chdir.c,$(LIBC_BOTTOM_HALF_ALL_SOURCES)) +LIBC_BOTTOM_HALF_ALL_SOURCES := $(LIBC_BOTTOM_HALF_ALL_SOURCES) $(LIBC_BOTTOM_HALF_SOURCES)/chdir.c + LIBWASI_EMULATED_MMAN_SOURCES = \ $(shell find $(LIBC_BOTTOM_HALF_DIR)/mman -name \*.c) LIBWASI_EMULATED_SIGNAL_SOURCES = \ diff --git a/expected/wasm32-wasi/defined-symbols.txt b/expected/wasm32-wasi/defined-symbols.txt index 79e34ff06..63296f18b 100644 --- a/expected/wasm32-wasi/defined-symbols.txt +++ b/expected/wasm32-wasi/defined-symbols.txt @@ -255,7 +255,9 @@ __wasilibc_ensure_environ __wasilibc_environ __wasilibc_environ __wasilibc_fd_renumber +__wasilibc_find_abspath __wasilibc_find_relpath +__wasilibc_find_relpath_alloc __wasilibc_initialize_environ __wasilibc_open_nomode __wasilibc_openat_nomode diff --git a/libc-bottom-half/headers/public/wasi/libc-find-relpath.h b/libc-bottom-half/headers/public/wasi/libc-find-relpath.h index dd70867be..fdb59e0db 100644 --- a/libc-bottom-half/headers/public/wasi/libc-find-relpath.h +++ b/libc-bottom-half/headers/public/wasi/libc-find-relpath.h @@ -10,8 +10,11 @@ extern "C" { * map. If a suitable entry is found, then the file descriptor for that entry * is returned. Additionally the absolute path of the directory's file * descriptor is returned in `abs_prefix` and the relative portion that needs - * to be opened is stored in `relative_path`. The size of the `relative_path` - * buffer is provided in the `relative_path_len` argument. + * to be opened is stored in `*relative_path`. + * + * The `relative_path` argument must be a pointer to a buffer valid for + * `relative_path_len` bytes, and this may be used as storage for the relative + * portion of the path being returned through `*relative_path`. * * Returns -1 on failure. Errno is set to either: * @@ -21,9 +24,26 @@ extern "C" { */ int __wasilibc_find_relpath(const char *path, const char **__restrict__ abs_prefix, - char *relative_path, + char **relative_path, size_t relative_path_len); +/** + * Look up the given `path`, which is interpreted as absolute, in the preopened + * directory map. If a suitable entry is found, then the file descriptor for + * that entry is returned. Additionally the relative portion of the path to + * where the fd is opened is returned through `relative_path`, the absolute + * prefix which was matched is stored to `abs_prefix`, and `relative_path` may + * be an interior pointer to the `abspath` string. + * + * Returns -1 on failure. Errno is set to either: + * + * * ENOMEM - failed to allocate memory for internal routines. + * * ENOENT - the `path` could not be found relative to any preopened dir. + */ +int __wasilibc_find_abspath(const char *abspath, + const char **__restrict__ abs_prefix, + const char **__restrict__ relative_path); + #ifdef __cplusplus } #endif diff --git a/libc-bottom-half/sources/chdir.c b/libc-bottom-half/sources/chdir.c index 4af62c0be..eeb9002c8 100644 --- a/libc-bottom-half/sources/chdir.c +++ b/libc-bottom-half/sources/chdir.c @@ -13,34 +13,24 @@ static int __wasilibc_cwd_mallocd = 0; int chdir(const char *path) { +#ifdef _REENTRANT +#error "chdir doesn't yet support multiple threads" +#endif static char *relative_buf = NULL; static size_t relative_buf_len = 0; // Find a preopen'd directory as well as a relative path we're anchored // from which we're changing directories to. const char *abs; - int parent_fd; - while (1) { - parent_fd = __wasilibc_find_relpath(path, &abs, relative_buf, relative_buf_len); - if (parent_fd != -1) - break; - if (errno != ERANGE) - return -1; - size_t new_len = relative_buf_len == 0 ? 16 : 2 * relative_buf_len; - relative_buf = realloc(relative_buf, new_len); - if (relative_buf == NULL) { - errno = ENOMEM; - return -1; - } - relative_buf_len = new_len; - } + int parent_fd = __wasilibc_find_relpath_alloc(path, &abs, &relative_buf, &relative_buf_len, 1); + if (parent_fd == -1) + return -1; // Make sure that this directory we're accessing is indeed a directory. struct stat dirinfo; int ret = fstatat(parent_fd, relative_buf, &dirinfo, 0); - if (ret == -1) { + if (ret == -1) return -1; - } if (!S_ISDIR(dirinfo.st_mode)) { errno = ENOTDIR; return -1; @@ -48,16 +38,17 @@ int chdir(const char *path) // Copy over our new absolute path into `__wasilibc_cwd`. Only copy over // the relative portion of the path if it's not equal to `.` - size_t len = strlen(abs); + size_t len = strlen(abs) + 1; int copy_relative = strcmp(relative_buf, ".") != 0; char *new_cwd = malloc(len + (copy_relative ? strlen(relative_buf) : 0)); if (new_cwd == NULL) { errno = ENOMEM; return -1; } - strcpy(new_cwd, abs); + new_cwd[0] = '/'; + strcpy(new_cwd + 1, abs); if (copy_relative) - strcpy(new_cwd + strlen(abs), relative_buf); + strcpy(new_cwd + 1 + strlen(abs), relative_buf); // And set our new malloc'd buffer into the global cwd, freeing the // previous one if necessary. @@ -68,3 +59,85 @@ int chdir(const char *path) __wasilibc_cwd_mallocd = 1; return 0; } + +static const char *make_absolute(const char *path) { + static char *make_absolute_buf = NULL; + static size_t make_absolute_len = 0; + + // If this path is absolute, then we return it as-is. + if (path[0] == '/') { + return path; + } + + // If the path is empty, or points to the current directory, then return + // the current directory. + if (path[0] == 0 || !strcmp(path, ".") || !strcmp(path, "./")) { + return __wasilibc_cwd; + } + + // If the path starts with `./` then we won't be appending that to the cwd. + if (path[0] == '.' && path[1] == '/') + path += 2; + + // Otherwise we'll take the current directory, add a `/`, and then add the + // input `path`. Note that this doesn't do any normalization (like removing + // `/./`). + size_t cwd_len = strlen(__wasilibc_cwd); + size_t path_len = strlen(path); + int need_slash = __wasilibc_cwd[cwd_len - 1] == '/' ? 0 : 1; + size_t alloc_len = cwd_len + path_len + 1 + need_slash; + if (alloc_len > make_absolute_len) { + make_absolute_buf = realloc(make_absolute_buf, alloc_len); + if (make_absolute_buf == NULL) + return NULL; + make_absolute_len = alloc_len; + } + strcpy(make_absolute_buf, __wasilibc_cwd); + if (need_slash) + strcpy(make_absolute_buf + cwd_len, "/"); + strcpy(make_absolute_buf + cwd_len + need_slash, path); + return make_absolute_buf; +} + +// Helper function defined only in this object file and weakly referenced from +// `preopens.c` and `posix.c` This function isn't necessary unless `chdir` is +// pulled in because all paths are otherwise absolute or relative to the root. +int __wasilibc_find_relpath_alloc( + const char *path, + const char **abs_prefix, + char **relative_buf, + size_t *relative_buf_len, + int can_realloc +) { + // First, make our path absolute taking the cwd into account. + const char *abspath = make_absolute(path); + if (abspath == NULL) { + errno = ENOMEM; + return -1; + } + + // Next use our absolute path and split it. Find the preopened `fd` parent + // directory and set `abs_prefix`. Next up we'll be trying to fit `rel` + // into `relative_buf`. + const char *rel; + int fd = __wasilibc_find_abspath(abspath, abs_prefix, &rel); + if (fd == -1) + return -1; + + size_t rel_len = strlen(rel); + if (*relative_buf_len < rel_len + 1) { + if (!can_realloc) { + errno = ERANGE; + return -1; + } + char *tmp = realloc(*relative_buf, rel_len + 1); + if (tmp == NULL) { + errno = ENOMEM; + return -1; + } + *relative_buf = tmp; + *relative_buf_len = rel_len + 1; + } + strcpy(*relative_buf, rel); + return fd; +} diff --git a/libc-bottom-half/sources/posix.c b/libc-bottom-half/sources/posix.c index 89bfd2192..4ac1dd79f 100644 --- a/libc-bottom-half/sources/posix.c +++ b/libc-bottom-half/sources/posix.c @@ -11,40 +11,35 @@ #include #include +int __wasilibc_find_relpath_alloc( + const char *path, + const char **abs, + char **relative, + size_t *relative_len, + int can_realloc +) __attribute__((weak)); + static int find_relpath2( const char *path, - const char **relative, - char **relative_buf, - size_t *relative_buf_len + char **relative, + size_t *relative_len ) { + // See comments in `preopens.c` for what this trick is doing. const char *abs; - - while (1) { - *relative = *relative_buf; - int dirfd = __wasilibc_find_relpath(path, &abs, *relative_buf, *relative_buf_len); - if (dirfd != -1) - return dirfd; - if (errno != ERANGE) - return -1; - size_t new_len = *relative_buf_len == 0 ? 16 : 2 * *relative_buf_len; - char *tmp = realloc(*relative_buf, new_len); - if (tmp == NULL) { - errno = ENOMEM; - return -1; - } - *relative_buf = tmp; - *relative_buf_len = new_len; - } + if (__wasilibc_find_relpath_alloc) + return __wasilibc_find_relpath_alloc(path, &abs, relative, relative_len, 1); + return __wasilibc_find_relpath(path, &abs, relative, *relative_len); } // Helper to call `__wasilibc_find_relpath` and return an already-managed // pointer for the `relative` path. This function is not reentrant since the // `relative` pointer will point to static data that cannot be reused until // `relative` is no longer used. -static int find_relpath(const char *path, const char **relative) { +static int find_relpath(const char *path, char **relative) { static char *relative_buf = NULL; static size_t relative_buf_len = 0; - return find_relpath2(path, relative, &relative_buf, &relative_buf_len); + *relative = relative_buf; + return find_relpath2(path, relative, &relative_buf_len); } int open(const char *path, int oflag, ...) { @@ -55,7 +50,7 @@ int open(const char *path, int oflag, ...) { // See the documentation in libc.h int __wasilibc_open_nomode(const char *path, int oflag) { - const char *relative_path; + char *relative_path; int dirfd = find_relpath(path, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. @@ -68,7 +63,7 @@ int __wasilibc_open_nomode(const char *path, int oflag) { } int access(const char *path, int amode) { - const char *relative_path; + char *relative_path; int dirfd = find_relpath(path, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. @@ -85,7 +80,7 @@ ssize_t readlink( char *restrict buf, size_t bufsize) { - const char *relative_path; + char *relative_path; int dirfd = find_relpath(path, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. @@ -98,7 +93,7 @@ ssize_t readlink( } int stat(const char *restrict path, struct stat *restrict buf) { - const char *relative_path; + char *relative_path; int dirfd = find_relpath(path, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. @@ -111,7 +106,7 @@ int stat(const char *restrict path, struct stat *restrict buf) { } int lstat(const char *restrict path, struct stat *restrict buf) { - const char *relative_path; + char *relative_path; int dirfd = find_relpath(path, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. @@ -124,7 +119,7 @@ int lstat(const char *restrict path, struct stat *restrict buf) { } int utime(const char *path, const struct utimbuf *times) { - const char *relative_path; + char *relative_path; int dirfd = find_relpath(path, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. @@ -143,7 +138,7 @@ int utime(const char *path, const struct utimbuf *times) { } int unlink(const char *path) { - const char *relative_path; + char *relative_path; int dirfd = find_relpath(path, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. @@ -159,7 +154,7 @@ int unlink(const char *path) { } int rmdir(const char *path) { - const char *relative_path; + char *relative_path; int dirfd = find_relpath(path, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. @@ -172,7 +167,7 @@ int rmdir(const char *path) { } int remove(const char *path) { - const char *relative_path; + char *relative_path; int dirfd = find_relpath(path, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. @@ -195,7 +190,7 @@ int remove(const char *path) { } int mkdir(const char *path, mode_t mode) { - const char *relative_path; + char *relative_path; int dirfd = find_relpath(path, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. @@ -208,7 +203,7 @@ int mkdir(const char *path, mode_t mode) { } DIR *opendir(const char *dirname) { - const char *relative_path; + char *relative_path; int dirfd = find_relpath(dirname, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. @@ -226,7 +221,7 @@ int scandir( int (*filter)(const struct dirent *), int (*compar)(const struct dirent **, const struct dirent **) ) { - const char *relative_path; + char *relative_path; int dirfd = find_relpath(dir, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. @@ -239,7 +234,7 @@ int scandir( } int symlink(const char *target, const char *linkpath) { - const char *relative_path; + char *relative_path; int dirfd = find_relpath(linkpath, &relative_path); // If we can't find a preopen for it, indicate that we lack capabilities. @@ -251,15 +246,14 @@ int symlink(const char *target, const char *linkpath) { return symlinkat(target, dirfd, relative_path); } -static char *relative_buf2 = NULL; -static size_t relative_buf_len2 = NULL; - int link(const char *old, const char *new) { - const char *old_relative_path; - int old_dirfd = find_relpath2(old, &old_relative_path, &relative_buf2, &relative_buf_len2); + static char *old_relative_buf2 = NULL; + static size_t old_relative_buf_len2 = 0; + char *old_relative_path; + int old_dirfd = find_relpath2(old, &old_relative_path, &old_relative_buf_len2); if (old_dirfd != -1) { - const char *new_relative_path; + char *new_relative_path; int new_dirfd = find_relpath(new, &new_relative_path); if (new_dirfd != -1) @@ -273,11 +267,13 @@ int link(const char *old, const char *new) { } int rename(const char *old, const char *new) { - const char *old_relative_path; - int old_dirfd = find_relpath2(old, &old_relative_path, &relative_buf2, &relative_buf_len2); + static char *old_relative_buf2 = NULL; + static size_t old_relative_buf_len2 = 0; + char *old_relative_path = old_relative_buf2; + int old_dirfd = find_relpath2(old, &old_relative_path, &old_relative_buf_len2); if (old_dirfd != -1) { - const char *new_relative_path; + char *new_relative_path; int new_dirfd = find_relpath(new, &new_relative_path); if (new_dirfd != -1) diff --git a/libc-bottom-half/sources/preopens.c b/libc-bottom-half/sources/preopens.c index 0c1fd4718..ea9964d65 100644 --- a/libc-bottom-half/sources/preopens.c +++ b/libc-bottom-half/sources/preopens.c @@ -72,47 +72,23 @@ static int resize(void) { return 0; } -extern char *__wasilibc_cwd; - -// Note that this function is not reentrant, the returned pointer can only be -// used until the next call of this function. -static const char *make_absolute(const char *path) { - static char *make_absolute_buf = NULL; - static size_t make_absolute_len = 0; - - // If this path is absolute, then we return it as-is. - if (path[0] == '/') { - return path; - } - - // If the path is empty, or points to the current directory, then return - // the current directory. - if (path[0] == 0 || !strcmp(path, ".") || !strcmp(path, "./")) { - return __wasilibc_cwd; +// Normalize an absolute path. Removes leading `/` and leading `./`, so the +// first character is the start of a directory name. Additionally translate `.` +// to the empty string. +static const char *strip_prefixes(const char *path) { + while (1) { + if (path[0] == '/') { + path++; + } else if (path[0] == '.' && path[1] == '/') { + path += 2; + } else if (path[0] == '.' && path[1] == 0) { + path++; + } else { + break; + } } - // If the path starts with `./` then we won't be appending that to the cwd. - if (path[0] == '.' && path[1] == '/') - path += 2; - - // Otherwise we'll take the current directory, add a `/`, and then add the - // input `path`. Note that this doesn't do any normalization (like removing - // `/./`). - size_t cwd_len = strlen(__wasilibc_cwd); - size_t path_len = strlen(path); - int need_slash = __wasilibc_cwd[cwd_len - 1] == '/' ? 0 : 1; - size_t alloc_len = cwd_len + path_len + 1 + need_slash; - if (alloc_len > make_absolute_len) { - make_absolute_buf = realloc(make_absolute_buf, alloc_len); - if (make_absolute_buf == NULL) - return NULL; - make_absolute_len = alloc_len; - } - strcpy(make_absolute_buf, __wasilibc_cwd); - if (need_slash) - strcpy(make_absolute_buf + cwd_len, "/"); - strcpy(make_absolute_buf + cwd_len + need_slash, path); - return make_absolute_buf; + return path; } /// Register the given preopened file descriptor under the given path. @@ -124,7 +100,7 @@ static int internal_register_preopened_fd(__wasi_fd_t fd, const char *relprefix) if (num_preopens == preopen_capacity && resize() != 0) return -1; - char *prefix = strdup(make_absolute(relprefix)); + char *prefix = strdup(strip_prefixes(relprefix)); if (prefix == NULL) return -1; preopens[num_preopens++] = (preopen) { prefix, fd, }; @@ -159,23 +135,35 @@ int __wasilibc_register_preopened_fd(int fd, const char *prefix) { return internal_register_preopened_fd((__wasi_fd_t)fd, prefix); } +int __wasilibc_find_relpath_alloc( + const char *path, + const char **abs, + char **relative, + size_t *relative_len, + int can_realloc +) __attribute__((weak)); + // See the documentation in libc-find-relpath.h. -int __wasilibc_find_relpath(const char *relpath, - const char **restrict abs_prefix, - char *relative_path, +int __wasilibc_find_relpath(const char *path, + const char **abs_prefix, + char **relative_path, size_t relative_path_len) { - assert_invariants(); - const char *path = make_absolute(relpath); - if (!path) { - errno = ENOMEM; - return -1; - } + // If `chdir` is linked, whose object file defines this symbol, then we + // call that. Otherwise if the program can't `chdir` then `path` is + // absolute (or relative to the root dir), so we delegate to `find_abspath` + if (__wasilibc_find_relpath_alloc) + return __wasilibc_find_relpath_alloc(path, abs_prefix, relative_path, &relative_path_len, 0); + return __wasilibc_find_abspath(path, abs_prefix, (const char**) relative_path); +} +// See the documentation in libc-find-relpath.h. +int __wasilibc_find_abspath(const char *path, + const char **abs_prefix, + const char **relative_path) { // Search through the preopens table. Iterate in reverse so that more // recently added preopens take precedence over less recently addded ones. size_t match_len = 0; int fd = -1; - *abs_prefix = ""; for (size_t i = num_preopens; i > 0; --i) { const preopen *pre = &preopens[i - 1]; const char *prefix = pre->prefix; @@ -209,11 +197,7 @@ int __wasilibc_find_relpath(const char *relpath, if (*computed == '\0') computed = "."; - if (strlen(computed) > relative_path_len) { - errno = ERANGE; - return -1; - } - strcpy(relative_path, computed); + *relative_path = computed; return fd; } From a0c140fa9b70f67d2e8eb4ad99c5aa7e10e53c39 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 7 Aug 2020 09:20:00 -0700 Subject: [PATCH 04/11] Add threading compat --- libc-bottom-half/sources/chdir.c | 7 ++++--- libc-bottom-half/sources/getcwd.c | 5 +++++ libc-bottom-half/sources/posix.c | 12 ++++++------ 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/libc-bottom-half/sources/chdir.c b/libc-bottom-half/sources/chdir.c index eeb9002c8..4be9b5a35 100644 --- a/libc-bottom-half/sources/chdir.c +++ b/libc-bottom-half/sources/chdir.c @@ -8,14 +8,15 @@ #include #include +#ifdef _REENTRANT +#error "chdir doesn't yet support multiple threads" +#endif + extern char *__wasilibc_cwd; static int __wasilibc_cwd_mallocd = 0; int chdir(const char *path) { -#ifdef _REENTRANT -#error "chdir doesn't yet support multiple threads" -#endif static char *relative_buf = NULL; static size_t relative_buf_len = 0; diff --git a/libc-bottom-half/sources/getcwd.c b/libc-bottom-half/sources/getcwd.c index e572f3063..6a2080ec5 100644 --- a/libc-bottom-half/sources/getcwd.c +++ b/libc-bottom-half/sources/getcwd.c @@ -2,6 +2,11 @@ #include #include +// For threads this needs to synchronize with chdir +#ifdef _REENTRANT +#error "getcwd doesn't yet support multiple threads" +#endif + char *__wasilibc_cwd = "/"; char *getcwd(char *buf, size_t size) diff --git a/libc-bottom-half/sources/posix.c b/libc-bottom-half/sources/posix.c index 4ac1dd79f..0b20f984f 100644 --- a/libc-bottom-half/sources/posix.c +++ b/libc-bottom-half/sources/posix.c @@ -36,8 +36,8 @@ static int find_relpath2( // `relative` pointer will point to static data that cannot be reused until // `relative` is no longer used. static int find_relpath(const char *path, char **relative) { - static char *relative_buf = NULL; - static size_t relative_buf_len = 0; + static __thread char *relative_buf = NULL; + static __thread size_t relative_buf_len = 0; *relative = relative_buf; return find_relpath2(path, relative, &relative_buf_len); } @@ -247,8 +247,8 @@ int symlink(const char *target, const char *linkpath) { } int link(const char *old, const char *new) { - static char *old_relative_buf2 = NULL; - static size_t old_relative_buf_len2 = 0; + static __thread char *old_relative_buf2 = NULL; + static __thread size_t old_relative_buf_len2 = 0; char *old_relative_path; int old_dirfd = find_relpath2(old, &old_relative_path, &old_relative_buf_len2); @@ -267,8 +267,8 @@ int link(const char *old, const char *new) { } int rename(const char *old, const char *new) { - static char *old_relative_buf2 = NULL; - static size_t old_relative_buf_len2 = 0; + static __thread char *old_relative_buf2 = NULL; + static __thread size_t old_relative_buf_len2 = 0; char *old_relative_path = old_relative_buf2; int old_dirfd = find_relpath2(old, &old_relative_path, &old_relative_buf_len2); From 01c3dfd252e417f2f32ee6d1c6538d3338826437 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 7 Aug 2020 09:21:37 -0700 Subject: [PATCH 05/11] Collect `link`/`renameat` second path lookup --- libc-bottom-half/sources/posix.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/libc-bottom-half/sources/posix.c b/libc-bottom-half/sources/posix.c index 0b20f984f..8c89efd03 100644 --- a/libc-bottom-half/sources/posix.c +++ b/libc-bottom-half/sources/posix.c @@ -42,6 +42,14 @@ static int find_relpath(const char *path, char **relative) { return find_relpath2(path, relative, &relative_buf_len); } +// same as `find_relpath`, but uses another set of static variables to cache +static int find_relpath_alt(const char *path, char **relative) { + static __thread char *relative_buf = NULL; + static __thread size_t relative_buf_len = 0; + *relative = relative_buf; + return find_relpath2(path, relative, &relative_buf_len); +} + int open(const char *path, int oflag, ...) { // WASI libc's `openat` ignores the mode argument, so call a special // entrypoint which avoids the varargs calling convention. @@ -247,10 +255,8 @@ int symlink(const char *target, const char *linkpath) { } int link(const char *old, const char *new) { - static __thread char *old_relative_buf2 = NULL; - static __thread size_t old_relative_buf_len2 = 0; char *old_relative_path; - int old_dirfd = find_relpath2(old, &old_relative_path, &old_relative_buf_len2); + int old_dirfd = find_relpath_alt(old, &old_relative_path); if (old_dirfd != -1) { char *new_relative_path; @@ -267,10 +273,8 @@ int link(const char *old, const char *new) { } int rename(const char *old, const char *new) { - static __thread char *old_relative_buf2 = NULL; - static __thread size_t old_relative_buf_len2 = 0; - char *old_relative_path = old_relative_buf2; - int old_dirfd = find_relpath2(old, &old_relative_path, &old_relative_buf_len2); + char *old_relative_path; + int old_dirfd = find_relpath_alt(old, &old_relative_path); if (old_dirfd != -1) { char *new_relative_path; From 84ea27a8ec9b73874031869c199f231fe8209c96 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 10 Aug 2020 06:59:31 -0700 Subject: [PATCH 06/11] Update comments about chdir.c in makefile --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 0a1c76457..899469512 100644 --- a/Makefile +++ b/Makefile @@ -45,10 +45,10 @@ LIBC_BOTTOM_HALF_ALL_SOURCES = \ $(shell find $(LIBC_BOTTOM_HALF_CLOUDLIBC_SRC) -name \*.c) \ $(shell find $(LIBC_BOTTOM_HALF_SOURCES) -name \*.c) -# Move `chdir.c` to the end of libc.a since the symbols it provides are used to -# satisfy weak references elsewhere in libc.a, and we don't want to eagerly -# satisfy those references unless `chdir` is pulled in. Some more discussion of -# this can be found on #214 +# FIXME(https://reviews.llvm.org/D85567) - due to a bug in LLD the weak +# references to a function defined in `chdir.c` only work if `chdir.c` is at the +# end of the archive, but once that LLD review lands and propagates into LLVM +# then we don't have to do this. LIBC_BOTTOM_HALF_ALL_SOURCES := $(filter-out $(LIBC_BOTTOM_HALF_SOURCES)/chdir.c,$(LIBC_BOTTOM_HALF_ALL_SOURCES)) LIBC_BOTTOM_HALF_ALL_SOURCES := $(LIBC_BOTTOM_HALF_ALL_SOURCES) $(LIBC_BOTTOM_HALF_SOURCES)/chdir.c From b4a0304a71c6437c157b9ae31635c82e76fe0eb9 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 25 Nov 2020 09:46:00 -0800 Subject: [PATCH 07/11] Move definition of `__wasilibc_find_relpath_alloc` to header --- .../headers/public/wasi/libc-find-relpath.h | 18 ++++++++++++++++++ libc-bottom-half/sources/posix.c | 8 -------- libc-bottom-half/sources/preopens.c | 8 -------- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/libc-bottom-half/headers/public/wasi/libc-find-relpath.h b/libc-bottom-half/headers/public/wasi/libc-find-relpath.h index fdb59e0db..0ab43588d 100644 --- a/libc-bottom-half/headers/public/wasi/libc-find-relpath.h +++ b/libc-bottom-half/headers/public/wasi/libc-find-relpath.h @@ -44,6 +44,24 @@ int __wasilibc_find_abspath(const char *abspath, const char **__restrict__ abs_prefix, const char **__restrict__ relative_path); +/** + * Same as `__wasilibc_find_relpath`, except that this function will interpret + * `relative` as a malloc'd buffer that will be `realloc`'d to the appropriate + * size to contain the relative path. + * + * Note that this is a weak symbol and if it's not defined you can use + * `__wasilibc_find_relpath`. The weak-nature of this symbols means that if it's + * not otherwise included in the compilation then `chdir` wasn't used an there's + * no need for this symbol. + */ +int __wasilibc_find_relpath_alloc( + const char *path, + const char **abs, + char **relative, + size_t *relative_len, + int can_realloc +) __attribute__((weak)); + #ifdef __cplusplus } #endif diff --git a/libc-bottom-half/sources/posix.c b/libc-bottom-half/sources/posix.c index 8c89efd03..388a57d42 100644 --- a/libc-bottom-half/sources/posix.c +++ b/libc-bottom-half/sources/posix.c @@ -11,14 +11,6 @@ #include #include -int __wasilibc_find_relpath_alloc( - const char *path, - const char **abs, - char **relative, - size_t *relative_len, - int can_realloc -) __attribute__((weak)); - static int find_relpath2( const char *path, char **relative, diff --git a/libc-bottom-half/sources/preopens.c b/libc-bottom-half/sources/preopens.c index ea9964d65..c93edf13b 100644 --- a/libc-bottom-half/sources/preopens.c +++ b/libc-bottom-half/sources/preopens.c @@ -135,14 +135,6 @@ int __wasilibc_register_preopened_fd(int fd, const char *prefix) { return internal_register_preopened_fd((__wasi_fd_t)fd, prefix); } -int __wasilibc_find_relpath_alloc( - const char *path, - const char **abs, - char **relative, - size_t *relative_len, - int can_realloc -) __attribute__((weak)); - // See the documentation in libc-find-relpath.h. int __wasilibc_find_relpath(const char *path, const char **abs_prefix, From 4a3e74e0235ec74dbf24b544f08b1be45ebc00ba Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 25 Nov 2020 09:46:14 -0800 Subject: [PATCH 08/11] Expand comments --- libc-bottom-half/sources/preopens.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libc-bottom-half/sources/preopens.c b/libc-bottom-half/sources/preopens.c index c93edf13b..404206702 100644 --- a/libc-bottom-half/sources/preopens.c +++ b/libc-bottom-half/sources/preopens.c @@ -73,8 +73,9 @@ static int resize(void) { } // Normalize an absolute path. Removes leading `/` and leading `./`, so the -// first character is the start of a directory name. Additionally translate `.` -// to the empty string. +// first character is the start of a directory name. This works because our +// process always starts with a working directory of `/`. Additionally translate +// `.` to the empty string. static const char *strip_prefixes(const char *path) { while (1) { if (path[0] == '/') { From 226fed72f6140c651d9194c12cb6640008449c23 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 25 Nov 2020 09:53:29 -0800 Subject: [PATCH 09/11] Document the format of strings a bit more --- libc-bottom-half/headers/public/wasi/libc-find-relpath.h | 8 ++++++++ libc-bottom-half/sources/chdir.c | 8 ++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/libc-bottom-half/headers/public/wasi/libc-find-relpath.h b/libc-bottom-half/headers/public/wasi/libc-find-relpath.h index 0ab43588d..fa0592b59 100644 --- a/libc-bottom-half/headers/public/wasi/libc-find-relpath.h +++ b/libc-bottom-half/headers/public/wasi/libc-find-relpath.h @@ -16,6 +16,9 @@ extern "C" { * `relative_path_len` bytes, and this may be used as storage for the relative * portion of the path being returned through `*relative_path`. * + * See documentation on `__wasilibc_find_abspath` for more info about what the + * paths look like. + * * Returns -1 on failure. Errno is set to either: * * * ENOMEM - failed to allocate memory for internal routines. @@ -35,6 +38,9 @@ int __wasilibc_find_relpath(const char *path, * prefix which was matched is stored to `abs_prefix`, and `relative_path` may * be an interior pointer to the `abspath` string. * + * The `abs_prefix` returned string will not contain a leading `/`. + * Additionally the returned `relative_path` will not contain a leading `/`. + * * Returns -1 on failure. Errno is set to either: * * * ENOMEM - failed to allocate memory for internal routines. @@ -53,6 +59,8 @@ int __wasilibc_find_abspath(const char *abspath, * `__wasilibc_find_relpath`. The weak-nature of this symbols means that if it's * not otherwise included in the compilation then `chdir` wasn't used an there's * no need for this symbol. + * + * See documentation on `__wasilibc_find_relpath` for more information. */ int __wasilibc_find_relpath_alloc( const char *path, diff --git a/libc-bottom-half/sources/chdir.c b/libc-bottom-half/sources/chdir.c index 4be9b5a35..117bcff83 100644 --- a/libc-bottom-half/sources/chdir.c +++ b/libc-bottom-half/sources/chdir.c @@ -37,8 +37,12 @@ int chdir(const char *path) return -1; } - // Copy over our new absolute path into `__wasilibc_cwd`. Only copy over - // the relative portion of the path if it's not equal to `.` + // Create a string that looks like: + // + // __wasilibc_cwd = "/" + abs + "/" + relative_buf + // + // If `relative_buf` is equal to ".", however, we skip that and the slash + // before it. size_t len = strlen(abs) + 1; int copy_relative = strcmp(relative_buf, ".") != 0; char *new_cwd = malloc(len + (copy_relative ? strlen(relative_buf) : 0)); From 49672d68159d1e4c0297f32c05690ae8388c0e3e Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 25 Nov 2020 10:15:44 -0800 Subject: [PATCH 10/11] Fixup a few issues in path logic --- .../headers/public/wasi/libc-find-relpath.h | 6 ++++-- libc-bottom-half/sources/chdir.c | 11 +++++++---- libc-bottom-half/sources/preopens.c | 4 ++++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/libc-bottom-half/headers/public/wasi/libc-find-relpath.h b/libc-bottom-half/headers/public/wasi/libc-find-relpath.h index fa0592b59..445613f5a 100644 --- a/libc-bottom-half/headers/public/wasi/libc-find-relpath.h +++ b/libc-bottom-half/headers/public/wasi/libc-find-relpath.h @@ -38,8 +38,10 @@ int __wasilibc_find_relpath(const char *path, * prefix which was matched is stored to `abs_prefix`, and `relative_path` may * be an interior pointer to the `abspath` string. * - * The `abs_prefix` returned string will not contain a leading `/`. - * Additionally the returned `relative_path` will not contain a leading `/`. + * The `abs_prefix` returned string will not contain a leading `/`. Note that + * this may be the empty string. Additionally the returned `relative_path` will + * not contain a leading `/`. The `relative_path` return will not return an + * empty string, it will return `"."` instead if it would otherwise do so. * * Returns -1 on failure. Errno is set to either: * diff --git a/libc-bottom-half/sources/chdir.c b/libc-bottom-half/sources/chdir.c index 117bcff83..7820448ee 100644 --- a/libc-bottom-half/sources/chdir.c +++ b/libc-bottom-half/sources/chdir.c @@ -41,19 +41,22 @@ int chdir(const char *path) // // __wasilibc_cwd = "/" + abs + "/" + relative_buf // - // If `relative_buf` is equal to ".", however, we skip that and the slash - // before it. + // If `relative_buf` is equal to "." or `abs` is equal to the empty string, + // however, we skip that part and the middle slash. size_t len = strlen(abs) + 1; int copy_relative = strcmp(relative_buf, ".") != 0; - char *new_cwd = malloc(len + (copy_relative ? strlen(relative_buf) : 0)); + int mid = copy_relative && abs[0] != 0; + char *new_cwd = malloc(len + (copy_relative ? strlen(relative_buf) + mid: 0)); if (new_cwd == NULL) { errno = ENOMEM; return -1; } new_cwd[0] = '/'; strcpy(new_cwd + 1, abs); + if (mid) + new_cwd[strlen(abs) + 1] = '/'; if (copy_relative) - strcpy(new_cwd + 1 + strlen(abs), relative_buf); + strcpy(new_cwd + 1 + mid + strlen(abs), relative_buf); // And set our new malloc'd buffer into the global cwd, freeing the // previous one if necessary. diff --git a/libc-bottom-half/sources/preopens.c b/libc-bottom-half/sources/preopens.c index 404206702..8c6882646 100644 --- a/libc-bottom-half/sources/preopens.c +++ b/libc-bottom-half/sources/preopens.c @@ -153,6 +153,10 @@ int __wasilibc_find_relpath(const char *path, int __wasilibc_find_abspath(const char *path, const char **abs_prefix, const char **relative_path) { + // Strip leading `/` characters, the prefixes we're mataching won't have + // them. + while (*path == '/') + path++; // Search through the preopens table. Iterate in reverse so that more // recently added preopens take precedence over less recently addded ones. size_t match_len = 0; From bfc9411b0f2a83704f4124605921d82afd9eb55a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 25 Nov 2020 10:17:32 -0800 Subject: [PATCH 11/11] Fix GitHub Actions --- .github/workflows/main.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0270af6c8..07df901b3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,8 +18,8 @@ jobs: run: | curl -fsSLO https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/LLVM-10.0.0-win64.exe 7z x LLVM-10.0.0-win64.exe -y -o"llvm" - echo "::add-path::$(pwd)/llvm/bin" - echo "::set-env name=WASM_AR::$(pwd)/llvm/bin/llvm-ar.exe" + echo "$(pwd)/llvm/bin" >> $GITHUB_PATH + echo "WASM_AR=$(pwd)/llvm/bin/llvm-ar.exe" >> $GITHUB_ENV if: matrix.os == 'windows-latest' - name: Install llvm-nm (Windows) @@ -28,7 +28,7 @@ jobs: rustup update stable rustup default stable rustup component add llvm-tools-preview - echo "::set-env name=WASM_NM::$(rustc --print sysroot|sed 's|C:|/c|'|sed 's|\\|/|g')/lib/rustlib/x86_64-pc-windows-msvc/bin/llvm-nm.exe" + echo "WASM_NM=$(rustc --print sysroot|sed 's|C:|/c|'|sed 's|\\|/|g')/lib/rustlib/x86_64-pc-windows-msvc/bin/llvm-nm.exe" >> $GITHUB_ENV if: matrix.os == 'windows-latest' - name: Install clang (MacOS) @@ -36,8 +36,8 @@ jobs: run: | curl -sSfL https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/clang+llvm-10.0.0-x86_64-apple-darwin.tar.xz | tar xJf - export CLANG_DIR=`pwd`/clang+llvm-10.0.0-x86_64-apple-darwin/bin - echo "::add-path::$CLANG_DIR" - echo "::set-env name=WASM_CC::$CLANG_DIR/clang" + echo "$CLANG_DIR" >> $GITHUB_PATH + echo "WASM_CC=$CLANG_DIR/clang" >> $GITHUB_ENV if: matrix.os == 'macos-latest' - name: Install clang (Linux) @@ -45,8 +45,8 @@ jobs: run: | curl -sSfL https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/clang+llvm-10.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz | tar xJf - export CLANG_DIR=`pwd`/clang+llvm-10.0.0-x86_64-linux-gnu-ubuntu-18.04/bin - echo "::add-path::$CLANG_DIR" - echo "::set-env name=WASM_CC::$CLANG_DIR/clang" + echo "$CLANG_DIR" >> $GITHUB_PATH + echo "WASM_CC=$CLANG_DIR/clang" >> $GITHUB_ENV if: matrix.os == 'ubuntu-latest' - name: Build libc @@ -76,7 +76,7 @@ jobs: - name: Install Rust (macos) run: | curl https://sh.rustup.rs | sh -s -- -y - echo "##[add-path]$HOME/.cargo/bin" + echo "$HOME/.cargo/bin" >> $GITHUB_PATH if: matrix.os == 'macos-latest' - run: cargo fetch working-directory: tools/wasi-headers