]> git.proxmox.com Git - wasi-libc.git/commitdiff
Revamp and simplify the libpreopen code. (#110)
authorDan Gohman <sunfish@mozilla.com>
Mon, 4 Nov 2019 22:56:35 +0000 (14:56 -0800)
committerGitHub <noreply@github.com>
Mon, 4 Nov 2019 22:56:35 +0000 (14:56 -0800)
wasi-libc's copy of libpreopen has evolved so many local changes that
it's no longer worth keeping the upstream code structure and marking
changes with __wasilibc_unmodified_upstream.

This PR merges the source files into a single file, removes all
__wasilibc_unmodified_upstream code, eliminates the ability to
allocate multiple preopen lists, eliminates the need for
__wasilibc_init_preopen, eliminates the non-standard eaccess, and
makes several other cleanups. It also enables NDEBUG so that internal
assertions are disabled in release builds.

12 files changed:
Makefile
expected/wasm32-wasi/defined-symbols.txt
expected/wasm32-wasi/predefined-macros.txt
libc-bottom-half/crt/crt1.c
libc-bottom-half/libpreopen/include/libpreopen.h [deleted file]
libc-bottom-half/libpreopen/lib/internal.h [deleted file]
libc-bottom-half/libpreopen/lib/libpreopen.c [deleted file]
libc-bottom-half/libpreopen/lib/po_err.c [deleted file]
libc-bottom-half/libpreopen/lib/po_libc_wrappers.c [deleted file]
libc-bottom-half/libpreopen/lib/po_map.c [deleted file]
libc-bottom-half/libpreopen/libpreopen.c [new file with mode: 0644]
libc-top-half/musl/src/regex/tre.h

index 1670153633e8463b108d0999fce19f4c3ab54fb9..204f25f24371345bc56a6805f9a113053d52ab21 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@
 WASM_CC ?= clang
 WASM_NM ?= $(patsubst %clang,%llvm-nm,$(WASM_CC))
 WASM_AR ?= $(patsubst %clang,%llvm-ar,$(WASM_CC))
-WASM_CFLAGS ?= -O2
+WASM_CFLAGS ?= -O2 -DNDEBUG
 # The directory where we build the sysroot.
 SYSROOT ?= $(CURDIR)/sysroot
 # A directory to install to for "make install".
@@ -45,12 +45,10 @@ LIBC_BOTTOM_HALF_CLOUDLIBC_SRC_INC = $(LIBC_BOTTOM_HALF_CLOUDLIBC_SRC)/include
 LIBC_BOTTOM_HALF_HEADERS_PUBLIC = $(LIBC_BOTTOM_HALF_DIR)/headers/public
 LIBC_BOTTOM_HALF_HEADERS_PRIVATE = $(LIBC_BOTTOM_HALF_DIR)/headers/private
 LIBC_BOTTOM_HALF_LIBPREOPEN_DIR = $(LIBC_BOTTOM_HALF_DIR)/libpreopen
-LIBC_BOTTOM_HALF_LIBPREOPEN_LIB = $(LIBC_BOTTOM_HALF_LIBPREOPEN_DIR)/lib
-LIBC_BOTTOM_HALF_LIBPREOPEN_INC = $(LIBC_BOTTOM_HALF_LIBPREOPEN_DIR)/include
 LIBC_BOTTOM_HALF_SOURCES = $(LIBC_BOTTOM_HALF_DIR)/sources
 LIBC_BOTTOM_HALF_ALL_SOURCES = \
     $(shell find $(LIBC_BOTTOM_HALF_CLOUDLIBC_SRC) -name \*.c) \
-    $(LIBC_BOTTOM_HALF_LIBPREOPEN_LIB)/po_libc_wrappers.c \
+    $(LIBC_BOTTOM_HALF_LIBPREOPEN_DIR)/libpreopen.c \
     $(shell find $(LIBC_BOTTOM_HALF_SOURCES) -name \*.c)
 LIBWASI_EMULATED_MMAN_SOURCES = \
     $(shell find $(LIBC_BOTTOM_HALF_DIR)/mman -name \*.c)
@@ -364,9 +362,7 @@ $(DLMALLOC_OBJS): override WASM_CFLAGS += \
 startup_files $(LIBC_BOTTOM_HALF_ALL_OBJS): override WASM_CFLAGS += \
     -I$(LIBC_BOTTOM_HALF_HEADERS_PRIVATE) \
     -I$(LIBC_BOTTOM_HALF_CLOUDLIBC_SRC_INC) \
-    -I$(LIBC_BOTTOM_HALF_CLOUDLIBC_SRC) \
-    -I$(LIBC_BOTTOM_HALF_LIBPREOPEN_LIB) \
-    -I$(LIBC_BOTTOM_HALF_LIBPREOPEN_INC)
+    -I$(LIBC_BOTTOM_HALF_CLOUDLIBC_SRC)
 
 $(LIBC_TOP_HALF_ALL_OBJS) $(MUSL_PRINTSCAN_LONG_DOUBLE_OBJS) $(MUSL_PRINTSCAN_NO_FLOATING_POINT_OBJS): override WASM_CFLAGS += \
     -I$(LIBC_TOP_HALF_MUSL_SRC_DIR)/include \
index e80a453b01168d69d0109bf55b803dd95a86cbd5..2b6a0af34b5e804f3999e7b842ab97fedee6ba21 100644 (file)
@@ -251,7 +251,6 @@ __uselocale
 __utc
 __wasilibc_fd_renumber
 __wasilibc_find_relpath
-__wasilibc_init_preopen
 __wasilibc_populate_environ
 __wasilibc_register_preopened_fd
 __wasilibc_rmdirat
@@ -428,7 +427,6 @@ drand48
 drem
 dremf
 duplocale
-eaccess
 ecvt
 encrypt
 environ
index 52ce3ceeb35d15151fb13e7c58691280000a452f..ef860937c00068e33606f1ed8c4a6d1958278b63 100644 (file)
 #define NAN (0.0f/0.0f)
 #define NBBY 8
 #define NCARGS 131072
+#define NDEBUG 1
 #define ND_NA_FLAG_OVERRIDE 0x00000020
 #define ND_NA_FLAG_ROUTER 0x00000080
 #define ND_NA_FLAG_SOLICITED 0x00000040
 #define and_eq &=
 #define asin(x) __tg_real_complex(asin, (x))
 #define asinh(x) __tg_real_complex(asinh, (x))
-#define assert(x) ((void)((x) || (__assert_fail(#x, __FILE__, __LINE__, __func__),0)))
+#define assert(x) (void)0
 #define atan(x) __tg_real_complex(atan, (x))
 #define atan2(x,y) __tg_real_2(atan2, (x), (y))
 #define atanh(x) __tg_real_complex(atanh, (x))
index 247604ad687e67e328139dbd3e0454d49e6cf204..ddf352f5144e4b149f0301b1de62708fb2bef4d8 100644 (file)
@@ -11,8 +11,6 @@ extern void __prepare_for_exit(void);
 void _Exit(int) __attribute__((noreturn));
 
 static __wasi_errno_t populate_libpreopen(void) {
-    __wasilibc_init_preopen();
-
     // Skip stdin, stdout, and stderr, and count up until we reach an invalid
     // file descriptor.
     for (__wasi_fd_t fd = 3; fd != 0; ++fd) {
diff --git a/libc-bottom-half/libpreopen/include/libpreopen.h b/libc-bottom-half/libpreopen/include/libpreopen.h
deleted file mode 100644 (file)
index 163bce3..0000000
+++ /dev/null
@@ -1,215 +0,0 @@
-/**
- * @file   libpreopen.h
- * @brief  Public header for libpreopen
- *
- * Copyright (c) 2016 Stanley Uche Godfrey
- * Copyright (c) 2016, 2018 Jonathan Anderson
- * All rights reserved.
- *
- * This software was developed at Memorial University under the
- * NSERC Discovery program (RGPIN-2015-06048).
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef LIBPO_H
-#define LIBPO_H
-
-#ifdef __wasilibc_unmodified_upstream
-#include <sys/cdefs.h>
-#include <sys/capsicum.h>
-#endif
-
-#include <stdbool.h>
-
-
-__BEGIN_DECLS
-
-/**
- * @struct po_map
- * @brief  A mapping from paths to pre-opened directories.
- *
- * This type is opaque to clients, but it is reference-counted and can be
- * thought of as containing a set (with no particular ordering guarantees)
- * of path->dirfd mappings.
- */
-struct po_map;
-
-#ifdef __wasilibc_unmodified_upstream
-/**
- * A callback that can be used to inpect the contents of a @ref po_map.
- *
- * This callback can be invoked by iteration code to expose elements of a
- * @ref po_map (in no particular order).
- *
- * @returns   whether or not to continue iterating over the @ref po_map
- */
-typedef bool (po_map_iter_cb)(const char *dirname, int dirfd, cap_rights_t);
-
-/**
- * A simple @ref po_map_iter_cb that will print a @ref po_map's entries,
- * one per line.
- */
-po_map_iter_cb po_print_entry;
-#endif
-
-/**
- * A filesystem path, relative to a directory descriptor.
- */
-struct po_relpath {
-       /** The directory the path is relative to */
-       int dirfd;
-
-       /** The path, relative to the directory represented by @ref dirfd */
-       const char *relative_path;
-};
-#ifdef __wasilibc_unmodified_upstream
-#else
-// This functionality is exposed in a libc header, so include that header
-// and use its declarations.
-#include <wasi/libc-find-relpath.h>
-#endif
-
-/**
- * Create a @ref po_map of at least the specified capacity.
- *
- * The returned @ref po_map will have a reference count of 1.
- */
-#ifdef __wasilibc_unmodified_upstream
-#else
-static
-#endif
-struct po_map* po_map_create(int capacity);
-
-/**
- * Release a reference to a @ref po_map.
- *
- * This may cause memory to be freed.
- */
-#ifdef __wasilibc_unmodified_upstream
-#else
-static
-#endif
-void po_map_release(struct po_map *);
-
-#ifdef __wasilibc_unmodified_upstream
-/**
- * Iterate over a @ref po_map, invoking a callback for each element in the map.
- *
- * This function will cause @b callback to be invoked repeatedly, in no
- * particular order, until the entire map has been iterated over or until the
- * callback returns `false`.
- *
- * @return   number of elements iterated over
- */
-size_t po_map_foreach(const struct po_map*, po_map_iter_cb);
-#endif
-
-/**
- * Add an already-opened directory to a @ref po_map.
- *
- * @param   map     the map to add the path->fd mapping to
- * @param   path    the path that will map to this directory
- *                  (which may or may not be the path used to open it)
- * @param   fd      the directory descriptor (must be a directory!)
- */
-#ifdef __wasilibc_unmodified_upstream
-#else
-static
-#endif
-struct po_map* po_add(struct po_map *map, const char *path, int fd);
-
-#ifdef __wasilibc_unmodified_upstream
-/**
- * Pre-open a path and store it in a @ref po_map for later use.
- *
- * @param   map     the map to add the path->fd mapping to
- * @param   path    the path to pre-open (which may be a directory if
- *                  `O_DIRECTORY` is passed via @b flags)
- * @param   flags   flags to pass to `open(2)` / `openat(2)`
- * @param   ...     optional file mode, as accepted by `open(2)`
- *
- * @returns the file descriptor of the opened directory or -1 if
- *          @b path is not a directory or cannot be opened or if
- *          the @ref po_map cannot store the directory (e.g., resizing fails)
- */
-int po_preopen(struct po_map *map, const char *path, int flags, ...);
-#endif
-
-/**
- * Find a directory whose path is a prefix of @b path and (on platforms that
- * support Capsicum) that has the rights required by @b rights.
- *
- * @param   map     the map to look for a directory in
- * @param   path    the path we want to find a pre-opened prefix for
- * @param   rights  if non-NULL on a platform with Capsicum support,
- *                  the rights any directory descriptor must have to
- *                  qualify as a match
- * @returns a @ref po_relpath containing the descriptor of the best-match
- *          directory in the map (or -1 if none was found) and the remaining
- *          path, relative to the file (or undefined if no match found)
- */
-#ifdef __wasilibc_unmodified_upstream
-struct po_relpath po_find(struct po_map *map, const char *path,
-       cap_rights_t *rights);
-#else
-static struct po_relpath po_find(struct po_map *map, const char *path,
-       __wasi_rights_t rights_base, __wasi_rights_t rights_inheriting);
-#endif
-
-#ifdef __wasilibc_unmodified_upstream
-/**
- * Retrieve a message from with the last libpreopen error.
- *
- * @returns NULL if there are no errors, null-terminated string otherwise
- */
-const char* po_last_error(void);
-
-/**
- * Pack a `struct po_map` into a shared memory segment.
- *
- * To inherit a `po_map` across the process execution boundary, it needs to be
- * packed into an inheritable form such as a shared memory segment. This can
- * then be unpacked in the child process for direct access.
- *
- * @param map     the map to pack into shared memory
- *
- * @returns       a file descriptor of a shared memory segment
- *                (or -1 on error)
- */
-int po_pack(struct po_map *map);
-
-/**
- * Unpack a `struct po_map` from a file.
- *
- * Using the representation generated by `po_pack`, unpack a `po_map`
- * and make it available for normal usage.
- *
- * @param fd      a file containing a packed `po_map` representation
- */
-struct po_map* po_unpack(int fd);
-#endif
-
-__END_DECLS
-
-#endif /* !LIBPO_H */
-
diff --git a/libc-bottom-half/libpreopen/lib/internal.h b/libc-bottom-half/libpreopen/lib/internal.h
deleted file mode 100644 (file)
index 93d908b..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-/**
- * @file   internal.h
- * @brief  Declarations of internal data structures and functions
- *
- * @cond internal
- */
-
-/*-
- * Copyright (c) 2016-2017 Stanley Uche Godfrey
- * Copyright (c) 2016-2018 Jonathan Anderson
- * All rights reserved.
- *
- * This software was developed at Memorial University under the
- * NSERC Discovery program (RGPIN-2015-06048).
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef LIBPO_INTERNAL_H
-#define LIBPO_INTERNAL_H
-
-#ifdef __wasilibc_unmodified_upstream
-#include <sys/cdefs.h>
-#endif
-
-#ifdef __wasilibc_unmodified_upstream
-#ifdef WITH_CAPSICUM
-#include <sys/capsicum.h>
-#endif
-#else
-// We do Capsicum-style rights-checking, though we use the WASI API directly
-// rather than the Capsicum API.
-#define WITH_CAPSICUM
-#endif
-
-#include <assert.h>
-#include <stdbool.h>
-
-#include "libpreopen.h"
-
-/**
- * An entry in a po_map.
- *
- * @internal
- */
-struct po_map_entry {
-       /**
-        * The name this file or directory is mapped to.
-        *
-        * This name should look like a path, but it does not necessarily need
-        * match to match the path it was originally obtained from.
-        */
-       const char *name;
-
-       /** File descriptor (which may be a directory) */
-       int fd;
-
-#ifdef WITH_CAPSICUM
-       /** Capability rights associated with the file descriptor */
-#ifdef __wasilibc_unmodified_upstream
-       cap_rights_t rights;
-#else
-       __wasi_rights_t rights_base;
-       __wasi_rights_t rights_inheriting;
-#endif
-#endif
-};
-
-// Documented in external header file
-struct po_map {
-       //! @internal
-       int refcount;
-       struct po_map_entry *entries;
-       size_t capacity;
-       size_t length;
-};
-
-
-/**
- * Is a directory a prefix of a given path?
- *
- * @param   dir     a directory path, e.g., `/foo/bar`
- * @param   dirlen  the length of @b dir
- * @param   path    a path that may have @b dir as a prefix,
- *                  e.g., `/foo/bar/baz`
- *
- * @internal
- */
-#ifdef __wasilibc_unmodified_upstream
-#else
-static
-#endif
-bool   po_isprefix(const char *dir, size_t dirlen, const char *path);
-
-
-/**
- * Check that a @ref po_map is valid (assert out if it's not).
- *
- * @internal
- */
-#ifdef NDEBUG
-#define po_map_assertvalid(...)
-#else
-#ifdef __wasilibc_unmodified_upstream
-#else
-static
-#endif
-void   po_map_assertvalid(const struct po_map *);
-#endif
-
-/**
- * Enlarge a @ref po_map's capacity.
- *
- * This results in new memory being allocated and existing entries being copied.
- * If the allocation fails, the function will return NULL but the original
- * map will remain valid.
- *
- * @internal
- */
-#ifdef __wasilibc_unmodified_upstream
-#else
-static
-#endif
-struct po_map* po_map_enlarge(struct po_map *map);
-
-#ifdef __wasilibc_unmodified_upstream
-/**
- * Store an error message in the global "last error message" buffer.
- *
- * @internal
- */
-void po_errormessage(const char *msg);
-#endif
-
-/**
- * Set the default map used by the libpreopen libc wrappers.
- *
- * If there is an existing default map, it will be freed before it is replaced.
- * Passing NULL to this function will thus clear the default map.
- */
-#ifdef __wasilibc_unmodified_upstream
-void po_set_libc_map(struct po_map *);
-#endif
-
-#endif /* LIBPO_INTERNAL_H */
-
-/** @endcond */
diff --git a/libc-bottom-half/libpreopen/lib/libpreopen.c b/libc-bottom-half/libpreopen/lib/libpreopen.c
deleted file mode 100644 (file)
index c1190c8..0000000
+++ /dev/null
@@ -1,261 +0,0 @@
-/*-
- * Copyright (c) 2016 Stanley Uche Godfrey
- * Copyright (c) 2016, 2018 Jonathan Anderson
- * All rights reserved.
- *
- * This software was developed at Memorial University under the
- * NSERC Discovery program (RGPIN-2015-06048).
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/**
- * @file  libpreopen.c
- * Implementation of high-level libpreopen functions.
- *
- * The functions defined in this source file are the highest-level API calls
- * that client code will mostly use (plus po_map_create and po_map_release).
- * po_isprefix is also defined here because it doesn't fit anywhere else.
- */
-
-#include <assert.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-#ifdef __wasilibc_unmodified_upstream
-#else
-#include <fcntl.h>
-#endif
-
-#include "internal.h"
-
-
-#ifdef __wasilibc_unmodified_upstream
-#else
-static
-#endif
-struct po_map*
-po_add(struct po_map *map, const char *path, int fd)
-{
-       struct po_map_entry *entry;
-
-       po_map_assertvalid(map);
-
-       if (path == NULL || fd < 0) {
-               return (NULL);
-       }
-
-       if (map->length == map->capacity) {
-               map = po_map_enlarge(map);
-               if (map == NULL) {
-                       return (NULL);
-               }
-       }
-
-       entry = map->entries + map->length;
-       map->length++;
-
-       entry->name = strdup(path);
-       entry->fd = fd;
-
-#ifdef WITH_CAPSICUM
-#ifdef __wasilibc_unmodified_upstream
-       if (cap_rights_get(fd, &entry->rights) != 0) {
-               return (NULL);
-       }
-#else
-       __wasi_fdstat_t statbuf;
-       int r = __wasi_fd_fdstat_get(fd, &statbuf);
-       if (r != 0) {
-               errno = r;
-               return NULL; // TODO: Add an infallible way to get the rights?
-       }
-
-       entry->rights_base = statbuf.fs_rights_base;
-       entry->rights_inheriting = statbuf.fs_rights_inheriting;
-#endif
-#endif
-
-
-       po_map_assertvalid(map);
-
-       return (map);
-}
-
-#ifdef __wasilibc_unmodified_upstream
-struct po_relpath
-po_find(struct po_map* map, const char *path, cap_rights_t *rights)
-#else
-static struct po_relpath
-po_find(struct po_map* map, const char *path,
-        __wasi_rights_t rights_base, __wasi_rights_t rights_inheriting)
-#endif
-{
-       const char *relpath ;
-       struct po_relpath match = { .relative_path = NULL, .dirfd = -1 };
-       size_t bestlen = 0;
-       int best = -1;
-
-       po_map_assertvalid(map);
-
-       if (path == NULL) {
-               return (match);
-       }
-
-       for(size_t i = 0; i < map->length; i++) {
-               const struct po_map_entry *entry = map->entries + i;
-               const char *name = entry->name;
-#ifdef __wasilibc_unmodified_upstream
-               size_t len = strnlen(name, MAXPATHLEN);
-#else
-               size_t len = strlen(name);
-               bool any_matches = false;
-
-               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 && name[0] == '.' && name[1] == '/') {
-                               // The entry starts with "./", so skip that prefix.
-                               name += 2;
-                               len -= 2;
-                       } else if (len == 1 && name[0] == '.') {
-                               // The entry is ".", so match it as an empty string.
-                               name += 1;
-                               len -= 1;
-                       }
-               }
-#endif
-
-#ifdef __wasilibc_unmodified_upstream
-               if ((len <= bestlen) || !po_isprefix(name, len, path)) {
-#else
-               if ((any_matches && len <= bestlen) || !po_isprefix(name, len, path)) {
-#endif
-                       continue;
-               }
-
-#ifdef WITH_CAPSICUM
-#ifdef __wasilibc_unmodified_upstream
-               if (rights && !cap_rights_contains(&entry->rights, rights)) {
-#else
-               if ((rights_base & ~entry->rights_base) != 0 ||
-                   (rights_inheriting & ~entry->rights_inheriting) != 0) {
-#endif
-                       continue;
-               }
-#endif
-
-               best = entry->fd;
-               bestlen = len;
-#ifdef __wasilibc_unmodified_upstream
-#else
-               any_matches = true;
-#endif
-       }
-
-       relpath = path + bestlen;
-
-       while (*relpath == '/') {
-               relpath++;
-       }
-
-       if (*relpath == '\0') {
-               relpath = ".";
-       }
-
-       match.relative_path = relpath;
-       match.dirfd = best;
-
-       return match;
-}
-
-#ifdef __wasilibc_unmodified_upstream
-#else
-static
-#endif
-bool
-po_isprefix(const char *dir, size_t dirlen, const char *path)
-{
-       size_t i;
-       assert(dir != NULL);
-       assert(path != NULL);
-#ifdef __wasilibc_unmodified_upstream
-#else
-       // Allow an empty string as a prefix of any relative path.
-       if (path[0] != '/' && dirlen == 0)
-               return true;
-#endif
-       for (i = 0; i < dirlen; i++)
-       {
-               if (path[i] != dir[i])
-                       return false;
-       }
-#ifdef __wasilibc_unmodified_upstream
-#else
-       // Ignore trailing slashes in directory names.
-       while (i > 0 && dir[i - 1] == '/') {
-               --i;
-       }
-#endif
-       return path[i] == '/' || path[i] == '\0';
-}
-
-#ifdef __wasilibc_unmodified_upstream
-int
-po_preopen(struct po_map *map, const char *path, int flags, ...)
-{
-       va_list args;
-       int fd, mode;
-
-       va_start(args, flags);
-       mode = va_arg(args, int);
-
-       po_map_assertvalid(map);
-
-       if (path == NULL) {
-               return (-1);
-       }
-
-       fd = openat(AT_FDCWD, path, flags, mode);
-       if (fd == -1) {
-               return (-1);
-       }
-
-       if (po_add(map, path, fd) == NULL) {
-               return (-1);
-       }
-
-       po_map_assertvalid(map);
-
-       return (fd);
-}
-#endif
-
-#ifdef __wasilibc_unmodified_upstream
-bool
-po_print_entry(const char *name, int fd, cap_rights_t rights)
-{
-       printf(" - name: '%s', fd: %d, rights: <rights>\n",
-              name, fd);
-       return (true);
-}
-#endif
diff --git a/libc-bottom-half/libpreopen/lib/po_err.c b/libc-bottom-half/libpreopen/lib/po_err.c
deleted file mode 100644 (file)
index db3b90b..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/*-
- * Copyright (c) 2016 Stanley Uche Godfrey
- * Copyright (c) 2018 Jonathan Anderson
- * All rights reserved.
- *
- * This software was developed at Memorial University under the
- * NSERC Discovery program (RGPIN-2015-06048).
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/**
- * @file  po_err.c
- * @brief Error handling for libpreopen
- */
-
-#ifdef __wasilibc_unmodified_upstream
-#include <sys/cdefs.h>
-#include <sys/param.h>
-#include <sys/mman.h>
-#endif
-#include <sys/stat.h>
-#include <sys/types.h>
-#ifdef __wasilibc_unmodified_upstream
-#include <sys/wait.h>
-#endif
-
-#include <assert.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "internal.h"
-#include "libpreopen.h"
-
-#ifdef __wasilibc_unmodified_upstream
-/* Disable all this error reporting code. */
-static char error_buffer[1024];
-#endif
-
-#if !defined(NDEBUG)
-#ifdef __wasilibc_unmodified_upstream
-#else
-static
-#endif
-void
-po_map_assertvalid(const struct po_map *map)
-{
-       const struct po_map_entry *entry;
-       size_t i;
-
-       assert(map->refcount > 0);
-       assert(map->length <= map->capacity);
-       assert(map->entries != NULL || map->capacity == 0);
-
-       for (i = 0; i < map->length; i++) {
-               entry = map->entries + i;
-
-               assert(entry->name != NULL);
-               assert(entry->fd >= 0);
-       }
-}
-#endif /* !defined(NDEBUG) */
-
-#ifdef __wasilibc_unmodified_upstream
-void
-po_errormessage(const char *msg)
-{
-
-       snprintf(error_buffer, sizeof(error_buffer), "%s: error %d",
-               msg, errno);
-}
-
-const char*
-po_last_error()
-{
-
-       return (error_buffer);
-}
-#endif
diff --git a/libc-bottom-half/libpreopen/lib/po_libc_wrappers.c b/libc-bottom-half/libpreopen/lib/po_libc_wrappers.c
deleted file mode 100644 (file)
index 680699b..0000000
+++ /dev/null
@@ -1,717 +0,0 @@
-/*-
- * Copyright (c) 2016 Stanley Uche Godfrey
- * Copyright (c) 2018 Jonathan Anderson
- * All rights reserved.
- *
- * This software was developed at Memorial University under the
- * NSERC Discovery program (RGPIN-2015-06048).
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/**
- * @file   po_libc_wrappers.c
- * @brief  Wrappers of libc functions that access global variables.
- */
-
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-
-#include <fcntl.h>
-#ifdef __wasilibc_unmodified_upstream // dlfcn
-#include <dlfcn.h>
-#endif
-#include <stdarg.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#ifdef __wasilibc_unmodified_upstream
-#else
-#include <errno.h>
-#include <wasi/libc.h>
-#endif
-
-#include "internal.h"
-#ifdef __wasilibc_unmodified_upstream
-#else
-// Make all of the po_* implementation details private.
-#include "libpreopen.c"
-#include "po_map.c"
-#include "po_err.c"
-#endif
-
-/**
- * A default po_map that can be used implicitly by libc wrappers.
- *
- * @internal
- */
-static struct po_map *global_map;
-
-/**
- * Find a relative path within the po_map given by SHARED_MEMORYFD (if it
- * exists).
- *
- * @returns  a struct po_relpath with dirfd and relative_path as set by po_find
- *           if there is an available po_map, or AT_FDCWD/path otherwise
- */
-#ifdef __wasilibc_unmodified_upstream
-static struct po_relpath find_relative(const char *path, cap_rights_t *);
-#else
-static struct po_relpath find_relative(const char *path,
-                                       __wasi_rights_t rights_base,
-                                       __wasi_rights_t rights_inheriting);
-#endif
-
-/**
- * Get the map that was handed into the process via `SHARED_MEMORYFD`
- * (if it exists).
- */
-static struct po_map*  get_shared_map(void);
-
-
-/*
- * Wrappers around system calls:
- */
-
-/**
- * Capability-safe wrapper around the `_open(2)` system call.
- *
- * `_open(2)` accepts a path argument that can reference the global filesystem
- * namespace. This is not a capability-safe operation, so this wrapper function
- * attempts to look up the path (or a prefix of it) within the current global
- * po_map and converts the call into the capability-safe `openat(2)` if
- * possible. If the current po_map does not contain the sought-after path,
- * this wrapper will call `openat(AT_FDCWD, original_path, ...)`, which is
- * the same as the unwrapped `open(2)` call (i.e., will fail with `ECAPMODE`).
- */
-int
-#ifdef __wasilibc_unmodified_upstream
-_open(const char *path, int flags, ...)
-#else
-/* We just need a plain open definition. */
-open(const char *path, int flags, ...)
-#endif
-{
-       struct po_relpath rel;
-       va_list args;
-       int mode;
-
-#ifdef __wasilibc_unmodified_upstream
-       va_start(args, flags);
-       mode = va_arg(args, int);
-       rel = find_relative(path, NULL);
-#else
-       if (flags & O_CREAT) {
-               va_start(args, flags);
-               mode = va_arg(args, int);
-               va_end(args);
-       } else {
-               mode = 0;
-       }
-       rel = find_relative(path, __WASI_RIGHT_PATH_OPEN, 0);
-
-       // If we can't find a preopened directory handle to open this file with,
-       // indicate that the program lacks the capabilities.
-       if (rel.dirfd == -1) {
-               errno = ENOTCAPABLE;
-               return -1;
-       }
-#endif
-
-#ifdef __wasilibc_unmodified_upstream
-       // If the file is already opened, no need of relative opening!
-       if( strcmp(rel.relative_path,".") == 0 )
-               return dup(rel.dirfd);
-       else
-               return openat(rel.dirfd, rel.relative_path, flags, mode);
-#else
-       return openat(rel.dirfd, rel.relative_path, flags, mode);
-#endif
-}
-
-/**
- * Capability-safe wrapper around the `access(2)` system call.
- *
- * `access(2)` accepts a path argument that can reference the global filesystem
- * namespace. This is not a capability-safe operation, so this wrapper function
- * attempts to look up the path (or a prefix of it) within the current global
- * po_map and converts the call into the capability-safe `faccessat(2)` if
- * possible. If the current po_map does not contain the sought-after path,
- * this wrapper will call `faccessat(AT_FDCWD, original_path, ...)`, which is
- * the same as the unwrapped `access(2)` call (i.e., will fail with `ECAPMODE`).
- */
-int
-access(const char *path, int mode)
-{
-#ifdef __wasilibc_unmodified_upstream
-       struct po_relpath rel = find_relative(path, NULL);
-#else
-       struct po_relpath rel = find_relative(path, __WASI_RIGHT_PATH_FILESTAT_GET, 0);
-
-       // If we can't find a preopened directory handle to open this file with,
-       // indicate that the program lacks the capabilities.
-       if (rel.dirfd == -1) {
-               errno = ENOTCAPABLE;
-               return -1;
-       }
-#endif
-
-       return faccessat(rel.dirfd, rel.relative_path, mode,0);
-}
-
-#ifdef __wasilibc_unmodified_upstream
-/**
- * Capability-safe wrapper around the `connect(2)` system call.
- *
- * `connect(2)` accepts a path argument that can reference the global filesystem
- * namespace. This is not a capability-safe operation, so this wrapper function
- * attempts to look up the path (or a prefix of it) within the current global
- * po_map and converts the call into the capability-safe `connectat(2)` if
- * possible. If the current po_map does not contain the sought-after path, this
- * wrapper will call `connectat(AT_FDCWD, original_path, ...)`, which is the
- * same as the unwrapped `connect(2)` call (i.e., will fail with `ECAPMODE`).
- */
-int
-connect(int s, const struct sockaddr *name, socklen_t namelen)
-{
-       struct po_relpath rel;
-
-       if (name->sa_family == AF_UNIX) {
-           struct sockaddr_un *usock = (struct sockaddr_un *)name;
-#ifdef __wasilibc_unmodified_upstream
-           rel = find_relative(usock->sun_path, NULL);
-           strlcpy(usock->sun_path, rel.relative_path, sizeof(usock->sun_path));
-#else
-           rel = find_relative(usock->sun_path, __WASI_RIGHT_CONNECT, 0);
-           if (strlen(rel.relative_path) + 1 > sizeof(usock->sun_path)) {
-               errno = ENOMEM;
-               return -1;
-           }
-
-           // If we can't find a preopened directory handle to open this file with,
-           // indicate that the program lacks the capabilities.
-           if (rel.dirfd == -1) {
-               errno = ENOTCAPABLE;
-               return -1;
-           }
-
-           strcpy(usock->sun_path, rel.relative_path);
-#endif
-           return connectat(rel.dirfd, s, name, namelen);
-       }
-
-       return connectat(AT_FDCWD, s, name, namelen);
-}
-#endif
-
-/**
- * Capability-safe wrapper around the `eaccess(2)` system call.
- *
- * `eaccess(2)` accepts a path argument that can reference the global filesystem
- * namespace. This is not a capability-safe operation, so this wrapper function
- * attempts to look up the path (or a prefix of it) within the current global
- * po_map and converts the call into the capability-safe `faccessat(2)` if
- * possible. If the current po_map does not contain the sought-after path, this
- * wrapper will call `faccessat(AT_FDCWD, original_path, ...)`, which is the
- * same as the unwrapped `eaccess(2)` call (i.e., will fail with `ECAPMODE`).
- */
-int
-eaccess(const char *path, int mode)
-{
-#ifdef __wasilibc_unmodified_upstream
-       struct po_relpath rel = find_relative(path, NULL);
-#else
-       struct po_relpath rel = find_relative(path, __WASI_RIGHT_PATH_FILESTAT_GET, 0);
-
-       // If we can't find a preopened directory handle to open this file with,
-       // indicate that the program lacks the capabilities.
-       if (rel.dirfd == -1) {
-           errno = ENOTCAPABLE;
-           return -1;
-       }
-#endif
-
-       return faccessat(rel.dirfd, rel.relative_path, mode, 0);
-}
-
-/**
- * Capability-safe wrapper around the `lstat(2)` system call.
- *
- * `lstat(2)` accepts a path argument that can reference the global filesystem
- * namespace. This is not a capability-safe operation, so this wrapper function
- * attempts to look up the path (or a prefix of it) within the current global
- * po_map and converts the call into the capability-safe `fstatat(2)` if
- * possible. If the current po_map does not contain the sought-after path,
- * this wrapper will call `fstatat(AT_FDCWD, original_path, ...)`, which is
- * the same as the unwrapped `lstat(2)` call (i.e., will fail with `ECAPMODE`).
- */
-int
-lstat(const char *path, struct stat *st)
-{
-#ifdef __wasilibc_unmodified_upstream
-       struct po_relpath rel = find_relative(path, NULL);
-#else
-       struct po_relpath rel = find_relative(path, __WASI_RIGHT_PATH_FILESTAT_GET, 0);
-
-       // If we can't find a preopened directory handle to open this file with,
-       // indicate that the program lacks the capabilities.
-       if (rel.dirfd == -1) {
-           errno = ENOTCAPABLE;
-           return -1;
-       }
-#endif
-
-       return fstatat(rel.dirfd, rel.relative_path,st,AT_SYMLINK_NOFOLLOW);
-}
-
-#ifdef __wasilibc_unmodified_upstream
-/**
- * Capability-safe wrapper around the `open(2)` system call.
- *
- * `open(2)` will behave just like `_open(2)` if the varargs are unpacked and
- *  passed.
- */
-int
-open(const char *path, int flags, ...)
-{
-       va_list args;
-       int mode;
-
-       va_start(args, flags);
-       mode = va_arg(args, int);
-       return _open(path, flags, mode);
-}
-#endif
-
-/**
- * Capability-safe wrapper around the `rename(2)` system call.
- *
- * `rename(2)` accepts a path argument that can reference the global filesystem
- * namespace. This is not a capability-safe operation, so this wrapper function
- * attempts to look up the path (or a prefix of it) within the current global
- * po_map and converts the call into the capability-safe `renameat(2)` if
- * possible. If the current po_map does not contain the sought-after path,
- * this wrapper will call `renameat(AT_FDCWD, original_path, ...)`, which is
- * the same as the unwrapped `rename(2)` call (i.e., will fail with `ECAPMODE`).
- */
-int
-rename(const char *from, const char *to)
-{
-#ifdef __wasilibc_unmodified_upstream
-       struct po_relpath rel_from = find_relative(from, NULL);
-       struct po_relpath rel_to = find_relative(to, NULL);
-#else
-       struct po_relpath rel_from = find_relative(from, __WASI_RIGHT_PATH_RENAME_SOURCE, 0);
-       struct po_relpath rel_to = find_relative(to, __WASI_RIGHT_PATH_RENAME_TARGET, 0);
-
-       // If we can't find a preopened directory handle to open this file with,
-       // indicate that the program lacks the capabilities.
-       if (rel_from.dirfd == -1 || rel_to.dirfd == -1) {
-           errno = ENOTCAPABLE;
-           return -1;
-       }
-#endif
-
-       return renameat(rel_from.dirfd, rel_from.relative_path, rel_to.dirfd,
-               rel_to.relative_path);
-}
-
-/**
- * Capability-safe wrapper around the `stat(2)` system call.
- *
- * `stat(2)` accepts a path argument that can reference the global filesystem
- * namespace. This is not a capability-safe operation, so this wrapper function
- * attempts to look up the path (or a prefix of it) within the current global
- * po_map and converts the call into the capability-safe `fstatat(2)` if
- * possible. If the current po_map does not contain the sought-after path,
- * this wrapper will call `fstatat(AT_FDCWD, original_path, ...)`, which is
- * the same as the unwrapped `stat(2)` call (i.e., will fail with `ECAPMODE`).
- */
-int
-stat(const char *path, struct stat *st)
-{
-#ifdef __wasilibc_unmodified_upstream
-       struct po_relpath rel = find_relative(path, NULL);
-#else
-       struct po_relpath rel = find_relative(path, __WASI_RIGHT_PATH_FILESTAT_GET, 0);
-
-       // If we can't find a preopened directory handle to open this file with,
-       // indicate that the program lacks the capabilities.
-       if (rel.dirfd == -1) {
-           errno = ENOTCAPABLE;
-           return -1;
-       }
-#endif
-
-       return fstatat(rel.dirfd, rel.relative_path,st, AT_SYMLINK_NOFOLLOW);
-}
-
-/**
- * Capability-safe wrapper around the `unlink(2)` system call.
- *
- * `unlink(2)` accepts a path argument that can reference the global filesystem
- * namespace. This is not a capability-safe operation, so this wrapper function
- * attempts to look up the path (or a prefix of it) within the current global
- * po_map and converts the call into the capability-safe `unlinkat(2)` if
- * possible. If the current po_map does not contain the sought-after path,
- * this wrapper will call `unlinkat(AT_FDCWD, original_path, 0) which is
- * the same as the unwrapped `unlink(2)` call (i.e., will fail with `ECAPMODE`).
- */
-int
-unlink(const char *path)
-{
-#ifdef __wasilibc_unmodified_upstream
-       struct po_relpath rel = find_relative(path, NULL);
-#else
-       struct po_relpath rel_pathname = find_relative(path, __WASI_RIGHT_PATH_UNLINK_FILE, 0);
-
-       // If we can't find a preopened directory handle to open this file with,
-       // indicate that the program lacks the capabilities.
-       if (rel_pathname.dirfd == -1) {
-           errno = ENOTCAPABLE;
-           return -1;
-       }
-#endif
-
-#ifdef __wasilibc_unmodified_upstream
-       return unlinkat(rel.dirfd, rel.relative_path, 0);
-#else
-       // `unlinkat` ends up importing `__wasi_path_remove_directory` even
-       // though we're not passing `AT_REMOVEDIR` here. So instead, use a
-       // specialized function which just imports `__wasi_path_unlink_file`.
-       return __wasilibc_unlinkat(rel_pathname.dirfd, rel_pathname.relative_path);
-#endif
-}
-
-/*
- * Wrappers around other libc calls:
- */
-
-#ifdef __wasilibc_unmodified_upstream
-/**
- * Capability-safe wrapper around the `dlopen(3)` libc function.
- *
- * `dlopen(3)` accepts a path argument that can reference the global filesystem
- * namespace. This is not a capability-safe operation, so this wrapper function
- * attempts to look up the path (or a prefix of it) within the current global
- * po_map and converts the call into the capability-safe `fdlopen(3)` if
- * possible. If the current po_map does not contain the sought-after path, this
- * wrapper will call `fdlopen(openat(AT_FDCWD, original_path), ...)`, which is
- * the same as the unwrapped `dlopen(3)` call (i.e., will fail with `ECAPMODE`).
- */
-void *
-dlopen(const char *path, int mode)
-{
-       struct po_relpath rel = find_relative(path, NULL);
-
-       return fdlopen(openat(rel.dirfd, rel.relative_path, 0, mode), mode);
-}
-#endif
-#ifdef __wasilibc_unmodified_upstream
-#else
-#include <dirent.h>
-
-int
-rmdir(const char *pathname)
-{
-       struct po_relpath rel_pathname = find_relative(pathname, __WASI_RIGHT_PATH_REMOVE_DIRECTORY, 0);
-
-       // If we can't find a preopened directory handle to open this file with,
-       // indicate that the program lacks the capabilities.
-       if (rel_pathname.dirfd == -1) {
-           errno = ENOTCAPABLE;
-           return -1;
-       }
-
-       return __wasilibc_rmdirat(rel_pathname.dirfd, rel_pathname.relative_path);
-}
-
-int
-remove(const char *pathname)
-{
-       struct po_relpath rel_pathname = find_relative(pathname,
-                                                      __WASI_RIGHT_PATH_UNLINK_FILE |
-                                                      __WASI_RIGHT_PATH_REMOVE_DIRECTORY,
-                                                       0);
-
-       // If searching for both file and directory rights failed, try searching
-       // for either individually.
-       if (rel_pathname.dirfd == -1) {
-               rel_pathname = find_relative(pathname, __WASI_RIGHT_PATH_UNLINK_FILE, 0);
-               if (rel_pathname.dirfd == -1) {
-                       rel_pathname = find_relative(pathname, __WASI_RIGHT_PATH_REMOVE_DIRECTORY, 0);
-               }
-       }
-
-       // If we can't find a preopened directory handle to open this file with,
-       // indicate that the program lacks the capabilities.
-       if (rel_pathname.dirfd == -1) {
-           errno = ENOTCAPABLE;
-           return -1;
-       }
-
-       int r = __wasilibc_unlinkat(rel_pathname.dirfd, rel_pathname.relative_path);
-       if (r != 0 && (errno == EISDIR || errno == ENOTCAPABLE))
-               r = __wasilibc_rmdirat(rel_pathname.dirfd, rel_pathname.relative_path);
-       return r;
-}
-
-int
-link(const char *oldpath, const char *newpath)
-{
-       struct po_relpath rel_oldpath = find_relative(oldpath, __WASI_RIGHT_PATH_LINK_SOURCE, 0);
-       struct po_relpath rel_newpath = find_relative(newpath, __WASI_RIGHT_PATH_LINK_TARGET, 0);
-
-       // If we can't find a preopened directory handle to open this file with,
-       // indicate that the program lacks the capabilities.
-       if (rel_oldpath.dirfd == -1 || rel_newpath.dirfd == -1) {
-           errno = ENOTCAPABLE;
-           return -1;
-       }
-
-       return linkat(rel_oldpath.dirfd, rel_oldpath.relative_path,
-                     rel_newpath.dirfd, rel_newpath.relative_path,
-                     0);
-}
-
-int
-mkdir(const char *pathname, mode_t mode)
-{
-       struct po_relpath rel_pathname = find_relative(pathname, __WASI_RIGHT_PATH_CREATE_DIRECTORY, 0);
-
-       // If we can't find a preopened directory handle to open this file with,
-       // indicate that the program lacks the capabilities.
-       if (rel_pathname.dirfd == -1) {
-           errno = ENOTCAPABLE;
-           return -1;
-       }
-
-       return mkdirat(rel_pathname.dirfd, rel_pathname.relative_path, mode);
-}
-
-DIR *
-opendir(const char *name)
-{
-       struct po_relpath rel_name = find_relative(name, __WASI_RIGHT_PATH_OPEN, 0);
-
-       // If we can't find a preopened directory handle to open this file with,
-       // indicate that the program lacks the capabilities.
-       if (rel_name.dirfd == -1) {
-           errno = ENOTCAPABLE;
-           return NULL;
-       }
-
-       return opendirat(rel_name.dirfd, rel_name.relative_path);
-}
-
-ssize_t
-readlink(const char *pathname, char *buf, size_t bufsiz)
-{
-       struct po_relpath rel_pathname = find_relative(pathname, __WASI_RIGHT_PATH_READLINK, 0);
-
-       // If we can't find a preopened directory handle to open this file with,
-       // indicate that the program lacks the capabilities.
-       if (rel_pathname.dirfd == -1) {
-           errno = ENOTCAPABLE;
-           return -1;
-       }
-
-       return readlinkat(rel_pathname.dirfd, rel_pathname.relative_path,
-                         buf, bufsiz);
-}
-
-int
-scandir(const char *dirp, struct dirent ***namelist,
-       int (*filter)(const struct dirent *),
-       int (*compar)(const struct dirent **, const struct dirent **))
-{
-       struct po_relpath rel_dirp = find_relative(dirp,
-                                                  __WASI_RIGHT_PATH_OPEN,
-                                                  __WASI_RIGHT_FD_READDIR);
-
-       // If we can't find a preopened directory handle to open this file with,
-       // indicate that the program lacks the capabilities.
-       if (rel_dirp.dirfd == -1) {
-           errno = ENOTCAPABLE;
-           return -1;
-       }
-
-       return scandirat(rel_dirp.dirfd, rel_dirp.relative_path,
-                        namelist, filter, compar);
-}
-
-int
-symlink(const char *target, const char *linkpath)
-{
-       struct po_relpath rel_linkpath = find_relative(linkpath, __WASI_RIGHT_PATH_SYMLINK, 0);
-
-       // If we can't find a preopened directory handle to open this file with,
-       // indicate that the program lacks the capabilities.
-       if (rel_linkpath.dirfd == -1) {
-           errno = ENOTCAPABLE;
-           return -1;
-       }
-
-       return symlinkat(target, rel_linkpath.dirfd, rel_linkpath.relative_path);
-}
-#endif
-
-#ifdef __wasilibc_unmodified_upstream
-#else
-static
-#endif
-/* Provide tests with mechanism to set our static po_map */
-void
-po_set_libc_map(struct po_map *map)
-{
-       po_map_assertvalid(map);
-
-       map->refcount += 1;
-
-       if (global_map != NULL) {
-               po_map_release(global_map);
-       }
-
-       global_map = map;
-}
-
-static struct po_relpath
-#ifdef __wasilibc_unmodified_upstream
-find_relative(const char *path, cap_rights_t *rights)
-#else
-find_relative(const char *path,
-              __wasi_rights_t rights_base,
-              __wasi_rights_t rights_inheriting)
-#endif
-{
-       struct po_relpath rel;
-       struct po_map *map;
-
-       map = get_shared_map();
-#ifdef __wasilibc_unmodified_upstream
-       if (map == NULL) {
-               rel.dirfd = AT_FDCWD;
-               rel.relative_path = path;
-       } else {
-               rel = po_find(map, path, NULL);
-       }
-#else
-       rel = po_find(map, path, rights_base, rights_inheriting);
-#endif
-
-       return (rel);
-}
-
-static struct po_map*
-get_shared_map()
-{
-#ifdef __wasilibc_unmodified_upstream
-       struct po_map *map;
-       char *end, *env;
-       long fd;
-
-       // Do we already have a default map?
-       if (global_map) {
-               po_map_assertvalid(global_map);
-               return (global_map);
-       }
-
-       // Attempt to unwrap po_map from a shared memory segment specified by
-       // SHARED_MEMORYFD
-       env = getenv("SHARED_MEMORYFD");
-       if (env == NULL || *env == '\0') {
-               return (NULL);
-       }
-
-       // We expect this environment variable to be an integer and nothing but
-       // an integer.
-       fd = strtol(env, &end, 10);
-       if (*end != '\0') {
-               return (NULL);
-       }
-
-       map = po_unpack(fd);
-       if (map == NULL) {
-               return (NULL);
-       }
-
-       global_map = map;
-
-       return (map);
-#else
-       assert(global_map);
-       po_map_assertvalid(global_map);
-       return global_map;
-#endif
-}
-#ifdef __wasilibc_unmodified_upstream
-#else
-void
-__wasilibc_init_preopen(void)
-{
-       global_map = po_map_create(4);
-       if (global_map == NULL)
-               __builtin_trap();
-       po_map_assertvalid(global_map);
-}
-
-/*
- * Register the given pre-opened file descriptor under the given path.
- */
-int
-__wasilibc_register_preopened_fd(int fd, const char *path)
-{
-       po_map_assertvalid(global_map);
-
-       if (path == NULL) {
-               return -1;
-       }
-
-#ifdef _REENTRANT
-#error "__wasilibc_register_preopened_fd doesn't yet support multiple threads"
-#endif
-
-       struct po_map *map = po_add(global_map, path, fd);
-       if (map == NULL) {
-               return -1;
-       }
-
-       po_map_assertvalid(map);
-       global_map = map;
-
-       return 0;
-}
-
-int __wasilibc_find_relpath(const char *path,
-                            __wasi_rights_t rights_base,
-                            __wasi_rights_t rights_inheriting,
-                            const char **relpath)
-{
-    struct po_relpath rel = find_relative(path, rights_base, rights_inheriting);
-    *relpath = rel.relative_path;
-    return rel.dirfd;
-}
-#endif
diff --git a/libc-bottom-half/libpreopen/lib/po_map.c b/libc-bottom-half/libpreopen/lib/po_map.c
deleted file mode 100644 (file)
index 6d5dfbc..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-/*-
- * Copyright (c) 2016 Stanley Uche Godfrey
- * Copyright (c) 2016, 2018 Jonathan Anderson
- * All rights reserved.
- *
- * This software was developed at Memorial University under the
- * NSERC Discovery program (RGPIN-2015-06048).
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/**
- * @file  po_map.c
- * @brief Implementation of po_map management functions
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "internal.h"
-
-#ifdef __wasilibc_unmodified_upstream
-#else
-static
-#endif
-struct po_map*
-po_map_create(int capacity)
-{
-       struct po_map *map;
-
-       map = malloc(sizeof(struct po_map));
-       if (map == NULL) {
-               return (NULL);
-       }
-
-       map->entries = calloc(sizeof(struct po_map_entry), capacity);
-       if (map->entries == NULL) {
-               free(map);
-               return (NULL);
-       }
-
-       map->refcount = 1;
-       map->capacity = capacity;
-       map->length = 0;
-
-       po_map_assertvalid(map);
-
-       return (map);
-}
-
-#ifdef __wasilibc_unmodified_upstream
-#else
-static
-#endif
-struct po_map*
-po_map_enlarge(struct po_map *map)
-{
-       struct po_map_entry *enlarged;
-       enlarged = calloc(sizeof(struct po_map_entry), 2 * map->capacity);
-       if (enlarged == NULL) {
-               return (NULL);
-       }
-       memcpy(enlarged, map->entries, map->length * sizeof(*enlarged));
-       free(map->entries);
-       map->entries = enlarged;
-       map->capacity = 2 * map->capacity;
-       return map;
-}
-
-#ifdef __wasilibc_unmodified_upstream
-size_t
-po_map_foreach(const struct po_map *map, po_map_iter_cb cb)
-{
-       struct po_map_entry *entry;
-       size_t n;
-
-       po_map_assertvalid(map);
-
-       for (n = 0; n < map->length; n++) {
-               entry = map->entries + n;
-
-               if (!cb(entry->name, entry->fd, entry->rights)) {
-                       break;
-               }
-       }
-
-       return (n);
-}
-#endif
-
-#ifdef __wasilibc_unmodified_upstream
-#else
-static
-#endif
-void
-po_map_release(struct po_map *map)
-{
-       if (map == NULL) {
-               return;
-       }
-
-       po_map_assertvalid(map);
-
-       map->refcount -= 1;
-
-       if (map->refcount == 0) {
-               free(map->entries);
-               free(map);
-       }
-}
diff --git a/libc-bottom-half/libpreopen/libpreopen.c b/libc-bottom-half/libpreopen/libpreopen.c
new file mode 100644 (file)
index 0000000..2579110
--- /dev/null
@@ -0,0 +1,551 @@
+//! Preopen functionality for WASI libc, for emulating support for absolute
+//! path names on top of WASI's OCap-style API.
+//!
+//! This file is derived from code in libpreopen, the upstream for which is:
+//!
+//!     https://github.com/musec/libpreopen
+//!
+//! and which bears the following copyrights and license:
+
+/*-
+ * Copyright (c) 2016-2017 Stanley Uche Godfrey
+ * Copyright (c) 2016-2018 Jonathan Anderson
+ * All rights reserved.
+ *
+ * This software was developed at Memorial University under the
+ * NSERC Discovery program (RGPIN-2015-06048).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef _REENTRANT
+#error "__wasilibc_register_preopened_fd doesn't yet support multiple threads"
+#endif
+
+#define _ALL_SOURCE
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <dirent.h>
+#include <assert.h>
+#include <wasi/libc.h>
+#include <wasi/libc-find-relpath.h>
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// POSIX API compatibility wrappers
+//
+////////////////////////////////////////////////////////////////////////////////
+
+int
+open(const char *path, int flags, ...)
+{
+    const char *relative_path;
+    int dirfd = __wasilibc_find_relpath(path, __WASI_RIGHT_PATH_OPEN, 0,
+                                        &relative_path);
+
+    // If we can't find a preopened directory handle to open this file with,
+    // indicate that the program lacks the capabilities.
+    if (dirfd == -1) {
+        errno = ENOTCAPABLE;
+        return -1;
+    }
+
+    // WASI libc's openat ignores the mode argument, so don't bother passing
+    // the actual mode value through.
+    return openat(dirfd, relative_path, flags);
+}
+
+int
+access(const char *path, int mode)
+{
+    const char *relative_path;
+    int dirfd = __wasilibc_find_relpath(path, __WASI_RIGHT_PATH_FILESTAT_GET, 0,
+                                        &relative_path);
+
+    // If we can't find a preopened directory handle to open this file with,
+    // indicate that the program lacks the capabilities.
+    if (dirfd == -1) {
+        errno = ENOTCAPABLE;
+        return -1;
+    }
+
+    return faccessat(dirfd, relative_path, mode, 0);
+}
+
+int
+lstat(const char *path, struct stat *st)
+{
+    const char *relative_path;
+    int dirfd = __wasilibc_find_relpath(path, __WASI_RIGHT_PATH_FILESTAT_GET, 0,
+                                        &relative_path);
+
+    // If we can't find a preopened directory handle to open this file with,
+    // indicate that the program lacks the capabilities.
+    if (dirfd == -1) {
+        errno = ENOTCAPABLE;
+        return -1;
+    }
+
+    return fstatat(dirfd, relative_path, st, AT_SYMLINK_NOFOLLOW);
+}
+
+int
+rename(const char *from, const char *to)
+{
+    const char *from_relative_path;
+    int from_dirfd = __wasilibc_find_relpath(from, __WASI_RIGHT_PATH_RENAME_SOURCE, 0,
+                                             &from_relative_path);
+
+    const char *to_relative_path;
+    int to_dirfd = __wasilibc_find_relpath(to, __WASI_RIGHT_PATH_RENAME_TARGET, 0,
+                                           &to_relative_path);
+
+    // If we can't find a preopened directory handle to open this file with,
+    // indicate that the program lacks the capabilities.
+    if (from_dirfd == -1 || to_dirfd == -1) {
+        errno = ENOTCAPABLE;
+        return -1;
+    }
+
+    return renameat(from_dirfd, from_relative_path, to_dirfd, to_relative_path);
+}
+
+int
+stat(const char *path, struct stat *st)
+{
+    const char *relative_path;
+    int dirfd = __wasilibc_find_relpath(path, __WASI_RIGHT_PATH_FILESTAT_GET, 0,
+                                        &relative_path);
+
+    // If we can't find a preopened directory handle to open this file with,
+    // indicate that the program lacks the capabilities.
+    if (dirfd == -1) {
+        errno = ENOTCAPABLE;
+        return -1;
+    }
+
+    return fstatat(dirfd, relative_path, st, AT_SYMLINK_NOFOLLOW);
+}
+
+int
+unlink(const char *path)
+{
+    const char *relative_path;
+    int dirfd = __wasilibc_find_relpath(path, __WASI_RIGHT_PATH_UNLINK_FILE, 0,
+                                        &relative_path);
+
+    // If we can't find a preopened directory handle to open this file with,
+    // indicate that the program lacks the capabilities.
+    if (dirfd == -1) {
+        errno = ENOTCAPABLE;
+        return -1;
+    }
+
+    // `unlinkat` ends up importing `__wasi_path_remove_directory` even
+    // though we're not passing `AT_REMOVEDIR` here. So instead, use a
+    // specialized function which just imports `__wasi_path_unlink_file`.
+    return __wasilibc_unlinkat(dirfd, relative_path);
+}
+
+int
+rmdir(const char *pathname)
+{
+    const char *relative_path;
+    int dirfd = __wasilibc_find_relpath(pathname, __WASI_RIGHT_PATH_REMOVE_DIRECTORY, 0,
+                                        &relative_path);
+
+    // If we can't find a preopened directory handle to open this file with,
+    // indicate that the program lacks the capabilities.
+    if (dirfd == -1) {
+        errno = ENOTCAPABLE;
+        return -1;
+    }
+
+    return __wasilibc_rmdirat(dirfd, relative_path);
+}
+
+int
+remove(const char *pathname)
+{
+    const char *relative_path;
+    int dirfd = __wasilibc_find_relpath(pathname,
+                                        __WASI_RIGHT_PATH_UNLINK_FILE |
+                                        __WASI_RIGHT_PATH_REMOVE_DIRECTORY,
+                                        0,
+                                        &relative_path);
+
+    // If searching for both file and directory rights failed, try searching
+    // for either individually.
+    if (dirfd == -1) {
+        dirfd = __wasilibc_find_relpath(pathname, __WASI_RIGHT_PATH_UNLINK_FILE, 0,
+                                        &relative_path);
+        if (dirfd == -1) {
+            dirfd = __wasilibc_find_relpath(pathname, __WASI_RIGHT_PATH_REMOVE_DIRECTORY, 0,
+                                            &relative_path);
+        }
+    }
+
+    // If we can't find a preopened directory handle to open this file with,
+    // indicate that the program lacks the capabilities.
+    if (dirfd == -1) {
+        errno = ENOTCAPABLE;
+        return -1;
+    }
+
+    int r = __wasilibc_unlinkat(dirfd, relative_path);
+    if (r != 0 && (errno == EISDIR || errno == ENOTCAPABLE))
+        r = __wasilibc_rmdirat(dirfd, relative_path);
+    return r;
+}
+
+int
+link(const char *oldpath, const char *newpath)
+{
+    const char *old_relative_path;
+    int old_dirfd = __wasilibc_find_relpath(oldpath, __WASI_RIGHT_PATH_LINK_SOURCE, 0,
+                                            &old_relative_path);
+
+    const char *new_relative_path;
+    int new_dirfd = __wasilibc_find_relpath(newpath, __WASI_RIGHT_PATH_LINK_TARGET, 0,
+                                            &new_relative_path);
+
+    // If we can't find a preopened directory handle to open this file with,
+    // indicate that the program lacks the capabilities.
+    if (old_dirfd == -1 || new_dirfd == -1) {
+        errno = ENOTCAPABLE;
+        return -1;
+    }
+
+    return linkat(old_dirfd, old_relative_path, new_dirfd, new_relative_path, 0);
+}
+
+int
+mkdir(const char *pathname, mode_t mode)
+{
+    const char *relative_path;
+    int dirfd = __wasilibc_find_relpath(pathname, __WASI_RIGHT_PATH_CREATE_DIRECTORY, 0,
+                                        &relative_path);
+
+    // If we can't find a preopened directory handle to open this file with,
+    // indicate that the program lacks the capabilities.
+    if (dirfd == -1) {
+        errno = ENOTCAPABLE;
+        return -1;
+    }
+
+    return mkdirat(dirfd, relative_path, mode);
+}
+
+DIR *
+opendir(const char *name)
+{
+    const char *relative_path;
+    int dirfd = __wasilibc_find_relpath(name, __WASI_RIGHT_PATH_OPEN, 0,
+                                        &relative_path);
+
+    // If we can't find a preopened directory handle to open this file with,
+    // indicate that the program lacks the capabilities.
+    if (dirfd == -1) {
+        errno = ENOTCAPABLE;
+        return NULL;
+    }
+
+    return opendirat(dirfd, relative_path);
+}
+
+ssize_t
+readlink(const char *pathname, char *buf, size_t bufsiz)
+{
+    const char *relative_path;
+    int dirfd = __wasilibc_find_relpath(pathname, __WASI_RIGHT_PATH_READLINK, 0,
+                                        &relative_path);
+
+    // If we can't find a preopened directory handle to open this file with,
+    // indicate that the program lacks the capabilities.
+    if (dirfd == -1) {
+        errno = ENOTCAPABLE;
+        return -1;
+    }
+
+    return readlinkat(dirfd, relative_path, buf, bufsiz);
+}
+
+int
+scandir(
+    const char *dirp,
+    struct dirent ***namelist,
+    int (*filter)(const struct dirent *),
+    int (*compar)(const struct dirent **, const struct dirent **))
+{
+    const char *relative_path;
+    int dirfd = __wasilibc_find_relpath(dirp,
+                                        __WASI_RIGHT_PATH_OPEN,
+                                        __WASI_RIGHT_FD_READDIR,
+                                        &relative_path);
+
+    // If we can't find a preopened directory handle to open this file with,
+    // indicate that the program lacks the capabilities.
+    if (dirfd == -1) {
+        errno = ENOTCAPABLE;
+        return -1;
+    }
+
+    return scandirat(dirfd, relative_path, namelist, filter, compar);
+}
+
+int
+symlink(const char *target, const char *linkpath)
+{
+    const char *relative_path;
+    int dirfd = __wasilibc_find_relpath(linkpath, __WASI_RIGHT_PATH_SYMLINK, 0,
+                                        &relative_path);
+
+    // If we can't find a preopened directory handle to open this file with,
+    // indicate that the program lacks the capabilities.
+    if (dirfd == -1) {
+        errno = ENOTCAPABLE;
+        return -1;
+    }
+
+    return symlinkat(target, dirfd, relative_path);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Support library
+//
+////////////////////////////////////////////////////////////////////////////////
+
+/// An entry in a po_map.
+struct po_map_entry {
+    /// The name this file or directory is mapped to.
+    ///
+    /// This name should look like a path, but it does not necessarily need
+    /// match to match the path it was originally obtained from.
+    const char *name;
+
+    /// File descriptor (which may be a directory)
+    int fd;
+
+    /// Capability rights associated with the file descriptor
+    __wasi_rights_t rights_base;
+    __wasi_rights_t rights_inheriting;
+};
+
+/// A vector of po_map_entry.
+struct po_map {
+    struct po_map_entry *entries;
+    size_t capacity;
+    size_t length;
+};
+
+static struct po_map global_map;
+
+/// Is a directory a prefix of a given path?
+///
+/// @param   dir     a directory path, e.g., `/foo/bar`
+/// @param   dirlen  the length of @b dir
+/// @param   path    a path that may have @b dir as a prefix,
+///                  e.g., `/foo/bar/baz`
+static bool
+po_isprefix(const char *dir, size_t dirlen, const char *path)
+{
+    assert(dir != NULL);
+    assert(path != NULL);
+
+    // Allow an empty string as a prefix of any relative path.
+    if (path[0] != '/' && dirlen == 0)
+        return true;
+
+    size_t i;
+    for (i = 0; i < dirlen; i++)
+    {
+        if (path[i] != dir[i])
+            return false;
+    }
+
+    // Ignore trailing slashes in directory names.
+    while (i > 0 && dir[i - 1] == '/') {
+        --i;
+    }
+
+    return path[i] == '/' || path[i] == '\0';
+}
+
+/// Enlarge a @ref po_map's capacity.
+///
+/// This results in new memory being allocated and existing entries being copied.
+/// If the allocation fails, the function will return -1.
+static int
+po_map_enlarge(void)
+{
+    size_t start_capacity = 4;
+    size_t new_capacity = global_map.capacity == 0 ?
+                          start_capacity :
+                          global_map.capacity * 2;
+
+    struct po_map_entry *enlarged =
+        calloc(sizeof(struct po_map_entry), new_capacity);
+    if (enlarged == NULL) {
+        return -1;
+    }
+    memcpy(enlarged, global_map.entries, global_map.length * sizeof(*enlarged));
+    free(global_map.entries);
+    global_map.entries = enlarged;
+    global_map.capacity = new_capacity;
+    return 0;
+}
+
+/// Assert that the global_map is valid.
+#ifdef NDEBUG
+#define po_map_assertvalid()
+#else
+static void
+po_map_assertvalid(void)
+{
+    const struct po_map_entry *entry;
+    size_t i;
+
+    assert(global_map.length <= global_map.capacity);
+    assert(global_map.entries != NULL || global_map.capacity == 0);
+
+    for (i = 0; i < global_map.length; i++) {
+        entry = &global_map.entries[i];
+
+        assert(entry->name != NULL);
+        assert(entry->fd >= 0);
+    }
+}
+#endif
+
+/// Register the given pre-opened file descriptor under the given path.
+int
+__wasilibc_register_preopened_fd(int fd, const char *path)
+{
+    po_map_assertvalid();
+
+    assert(fd >= 0);
+    assert(path != NULL);
+
+    if (global_map.length == global_map.capacity) {
+        int n = po_map_enlarge();
+
+        po_map_assertvalid();
+
+        if (n != 0) {
+            return n;
+        }
+    }
+
+    __wasi_fdstat_t statbuf;
+    int r = __wasi_fd_fdstat_get((__wasi_fd_t)fd, &statbuf);
+    if (r != 0) {
+        errno = r;
+        return -1; // TODO: Add an infallible way to get the rights?
+    }
+
+    const char *name = strdup(path);
+    if (name == NULL) {
+        return -1;
+    }
+
+    struct po_map_entry *entry = &global_map.entries[global_map.length++];
+
+    entry->name = name;
+    entry->fd = fd;
+    entry->rights_base = statbuf.fs_rights_base;
+    entry->rights_inheriting = statbuf.fs_rights_inheriting;
+
+    po_map_assertvalid();
+
+    return 0;
+}
+
+int
+__wasilibc_find_relpath(
+    const char *path,
+    __wasi_rights_t rights_base,
+    __wasi_rights_t rights_inheriting,
+    const char **relative_path)
+{
+    size_t bestlen = 0;
+    int best = -1;
+
+    po_map_assertvalid();
+
+    assert(path != NULL);
+    assert(relative_path != NULL);
+
+    bool any_matches = false;
+    for (size_t i = 0; i < global_map.length; i++) {
+        const struct po_map_entry *entry = &global_map.entries[i];
+        const char *name = entry->name;
+        size_t len = strlen(name);
+
+        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 && name[0] == '.' && name[1] == '/') {
+                // The entry starts with "./", so skip that prefix.
+                name += 2;
+                len -= 2;
+            } else if (len == 1 && name[0] == '.') {
+                // The entry is ".", so match it as an empty string.
+                name += 1;
+                len -= 1;
+            }
+        }
+
+        if ((any_matches && len <= bestlen) || !po_isprefix(name, len, path)) {
+            continue;
+        }
+
+        if ((rights_base & ~entry->rights_base) != 0 ||
+            (rights_inheriting & ~entry->rights_inheriting) != 0) {
+            continue;
+        }
+
+        best = entry->fd;
+        bestlen = len;
+        any_matches = true;
+    }
+
+    const char *relpath = path + bestlen;
+
+    while (*relpath == '/') {
+        relpath++;
+    }
+
+    if (*relpath == '\0') {
+        relpath = ".";
+    }
+
+    *relative_path = relpath;
+    return best;
+}
index 9aae851f617af2a577499ed554e3d43ed83764e4..aed64f3e7e0f7dc5c6bf820daf57b29cc5c15c89 100644 (file)
@@ -35,7 +35,9 @@
 
 #undef  TRE_MBSTATE
 
+#ifdef __wasilibc_unmodified_upstream // Allow NDEBUG to be predefined.
 #define NDEBUG
+#endif
 
 #define TRE_REGEX_T_FIELD __opaque
 typedef int reg_errcode_t;