@@ -25,6 +25,7 @@ typedef struct preopen {
2525} preopen ;
2626
2727/// A simple growable array of `preopen`.
28+ static _Atomic _Bool preopens_populated = false;
2829static preopen * preopens ;
2930static size_t num_preopens ;
3031static size_t preopen_capacity ;
@@ -100,35 +101,42 @@ static const char *strip_prefixes(const char *path) {
100101 return path ;
101102}
102103
103- /// Register the given preopened file descriptor under the given path.
104- ///
105- /// This function takes ownership of `prefix`.
106- static int internal_register_preopened_fd (__wasi_fd_t fd , const char * relprefix ) {
107- LOCK (lock );
108-
104+ /// Similar to `internal_register_preopened_fd_unlocked` but does not
105+ /// take a lock.
106+ static int internal_register_preopened_fd_unlocked (__wasi_fd_t fd , const char * relprefix ) {
109107 // Check preconditions.
110108 assert_invariants ();
111109 assert (fd != AT_FDCWD );
112110 assert (fd != -1 );
113111 assert (relprefix != NULL );
114112
115113 if (num_preopens == preopen_capacity && resize () != 0 ) {
116- UNLOCK (lock );
117114 return -1 ;
118115 }
119116
120117 char * prefix = strdup (strip_prefixes (relprefix ));
121118 if (prefix == NULL ) {
122- UNLOCK (lock );
123119 return -1 ;
124120 }
125121 preopens [num_preopens ++ ] = (preopen ) { prefix , fd , };
126122
127123 assert_invariants ();
128- UNLOCK (lock );
129124 return 0 ;
130125}
131126
127+ /// Register the given preopened file descriptor under the given path.
128+ ///
129+ /// This function takes ownership of `prefix`.
130+ static int internal_register_preopened_fd (__wasi_fd_t fd , const char * relprefix ) {
131+ LOCK (lock );
132+
133+ int r = internal_register_preopened_fd_unlocked (fd , relprefix );
134+
135+ UNLOCK (lock );
136+
137+ return r ;
138+ }
139+
132140/// Are the `prefix_len` bytes pointed to by `prefix` a prefix of `path`?
133141static bool prefix_matches (const char * prefix , size_t prefix_len , const char * path ) {
134142 // Allow an empty string as a prefix of any relative path.
@@ -152,6 +160,8 @@ static bool prefix_matches(const char *prefix, size_t prefix_len, const char *pa
152160
153161// See the documentation in libc.h
154162int __wasilibc_register_preopened_fd (int fd , const char * prefix ) {
163+ __wasilibc_populate_preopens ();
164+
155165 return internal_register_preopened_fd ((__wasi_fd_t )fd , prefix );
156166}
157167
@@ -172,6 +182,8 @@ int __wasilibc_find_relpath(const char *path,
172182int __wasilibc_find_abspath (const char * path ,
173183 const char * * abs_prefix ,
174184 const char * * relative_path ) {
185+ __wasilibc_populate_preopens ();
186+
175187 // Strip leading `/` characters, the prefixes we're mataching won't have
176188 // them.
177189 while (* path == '/' )
@@ -218,3 +230,84 @@ int __wasilibc_find_abspath(const char *path,
218230 * relative_path = computed ;
219231 return fd ;
220232}
233+
234+ __attribute__((constructor (51 )))
235+ void __wasilibc_populate_preopens (void ) {
236+ // Fast path: If the preopens are already initialized, do nothing.
237+ if (preopens_populated ) {
238+ return ;
239+ }
240+
241+ LOCK (lock );
242+
243+ // Check whether another thread initialized the preopens already.
244+ if (preopens_populated ) {
245+ UNLOCK (lock );
246+ return ;
247+ }
248+
249+ // Skip stdin, stdout, and stderr, and count up until we reach an invalid
250+ // file descriptor.
251+ for (__wasi_fd_t fd = 3 ; fd != 0 ; ++ fd ) {
252+ __wasi_prestat_t prestat ;
253+ __wasi_errno_t ret = __wasi_fd_prestat_get (fd , & prestat );
254+ if (ret == __WASI_ERRNO_BADF )
255+ break ;
256+ if (ret != __WASI_ERRNO_SUCCESS )
257+ goto oserr ;
258+ switch (prestat .tag ) {
259+ case __WASI_PREOPENTYPE_DIR : {
260+ char * prefix = malloc (prestat .u .dir .pr_name_len + 1 );
261+ if (prefix == NULL )
262+ goto software ;
263+
264+ // TODO: Remove the cast on `prefix` once the witx is updated with
265+ // char8 support.
266+ ret = __wasi_fd_prestat_dir_name (fd , (uint8_t * )prefix ,
267+ prestat .u .dir .pr_name_len );
268+ if (ret != __WASI_ERRNO_SUCCESS )
269+ goto oserr ;
270+ prefix [prestat .u .dir .pr_name_len ] = '\0' ;
271+
272+ if (internal_register_preopened_fd_unlocked (fd , prefix ) != 0 )
273+ goto software ;
274+ free (prefix );
275+
276+ break ;
277+ }
278+ default :
279+ break ;
280+ }
281+ }
282+
283+ // Preopens are now initialized.
284+ preopens_populated = true;
285+
286+ UNLOCK (lock );
287+
288+ return ;
289+ oserr :
290+ _Exit (EX_OSERR );
291+ software :
292+ _Exit (EX_SOFTWARE );
293+ }
294+
295+ void __wasilibc_reset_preopens (void ) {
296+ LOCK (lock );
297+
298+ if (num_preopens ) {
299+ for (int i = 0 ; i < num_preopens ; ++ i ) {
300+ free ((void * ) preopens [i ].prefix );
301+ }
302+ free (preopens );
303+ }
304+
305+ preopens_populated = false;
306+ preopens = NULL ;
307+ num_preopens = 0 ;
308+ preopen_capacity = 0 ;
309+
310+ assert_invariants ();
311+
312+ UNLOCK (lock );
313+ }
0 commit comments