• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <stdlib.h>
2 #include <string.h>
3 #include <sys/stat.h>
4 
5 #ifndef _WIN32
6 # include <sys/types.h>
7 #endif /* _WIN32 */
8 
9 #include "uv.h"
10 #include "fd_table.h"
11 #include "path_resolver.h"
12 #include "wasi_types.h"
13 #include "wasi_rights.h"
14 #include "uv_mapping.h"
15 #include "uvwasi_alloc.h"
16 
17 
uvwasi__insert_stdio(uvwasi_t * uvwasi,struct uvwasi_fd_table_t * table,const uvwasi_fd_t fd,const uvwasi_fd_t expected,const char * name)18 static uvwasi_errno_t uvwasi__insert_stdio(uvwasi_t* uvwasi,
19                                            struct uvwasi_fd_table_t* table,
20                                            const uvwasi_fd_t fd,
21                                            const uvwasi_fd_t expected,
22                                            const char* name) {
23   struct uvwasi_fd_wrap_t* wrap;
24   uvwasi_filetype_t type;
25   uvwasi_rights_t base;
26   uvwasi_rights_t inheriting;
27   uvwasi_errno_t err;
28 
29   err = uvwasi__get_filetype_by_fd(fd, &type);
30   if (err != UVWASI_ESUCCESS)
31     return err;
32 
33   err = uvwasi__get_rights(fd, UV_FS_O_RDWR, type, &base, &inheriting);
34   if (err != UVWASI_ESUCCESS)
35     return err;
36 
37   err = uvwasi_fd_table_insert(uvwasi,
38                                table,
39                                fd,
40                                NULL,
41                                name,
42                                name,
43                                type,
44                                base,
45                                inheriting,
46                                0,
47                                &wrap);
48   if (err != UVWASI_ESUCCESS)
49     return err;
50 
51   if (wrap->id != expected)
52     err = UVWASI_EBADF;
53 
54   uv_mutex_unlock(&wrap->mutex);
55   return err;
56 }
57 
58 
uvwasi_fd_table_insert(uvwasi_t * uvwasi,struct uvwasi_fd_table_t * table,uv_file fd,uv_tcp_t * sock,const char * mapped_path,const char * real_path,uvwasi_filetype_t type,uvwasi_rights_t rights_base,uvwasi_rights_t rights_inheriting,int preopen,struct uvwasi_fd_wrap_t ** wrap)59 uvwasi_errno_t uvwasi_fd_table_insert(uvwasi_t* uvwasi,
60                                       struct uvwasi_fd_table_t* table,
61                                       uv_file fd,
62                                       uv_tcp_t* sock,
63                                       const char* mapped_path,
64                                       const char* real_path,
65                                       uvwasi_filetype_t type,
66                                       uvwasi_rights_t rights_base,
67                                       uvwasi_rights_t rights_inheriting,
68                                       int preopen,
69                                       struct uvwasi_fd_wrap_t** wrap) {
70   struct uvwasi_fd_wrap_t* entry;
71   struct uvwasi_fd_wrap_t** new_fds;
72   uvwasi_errno_t err;
73   uint32_t new_size;
74   uint32_t index;
75   uint32_t i;
76   int r;
77   size_t mp_len;
78   char* mp_copy;
79   size_t rp_len;
80   char* rp_copy;
81   char* np_copy;
82 
83   if (type != UVWASI_FILETYPE_SOCKET_STREAM) {
84     mp_len = strlen(mapped_path);
85     rp_len = strlen(real_path);
86   } else {
87     mp_len = 0;
88     rp_len = 0;
89     rp_copy = NULL;
90     mp_copy = NULL;
91     np_copy = NULL;
92   }
93 
94   /* Reserve room for the mapped path, real path, and normalized mapped path. */
95   entry = (struct uvwasi_fd_wrap_t*)
96     uvwasi__malloc(uvwasi, sizeof(*entry) + mp_len + mp_len + rp_len + 3);
97   if (entry == NULL)
98     return UVWASI_ENOMEM;
99 
100   if (type != UVWASI_FILETYPE_SOCKET_STREAM) {
101     mp_copy = (char*)(entry + 1);
102     rp_copy = mp_copy + mp_len + 1;
103     np_copy = rp_copy + rp_len + 1;
104     memcpy(mp_copy, mapped_path, mp_len);
105     mp_copy[mp_len] = '\0';
106     memcpy(rp_copy, real_path, rp_len);
107     rp_copy[rp_len] = '\0';
108 
109     /* Calculate the normalized version of the mapped path, as it will be used for
110        any path calculations on this fd. Use the length of the mapped path as an
111        upper bound for the normalized path length. */
112     err = uvwasi__normalize_path(mp_copy, mp_len, np_copy, mp_len);
113     if (err) {
114       uvwasi__free(uvwasi, entry);
115       goto exit;
116     }
117   }
118 
119   uv_rwlock_wrlock(&table->rwlock);
120 
121   /* Check that there is room for a new item. If there isn't, grow the table. */
122   if (table->used >= table->size) {
123     new_size = table->size * 2;
124     new_fds = uvwasi__realloc(uvwasi, table->fds, new_size * sizeof(*new_fds));
125     if (new_fds == NULL) {
126       uvwasi__free(uvwasi, entry);
127       err = UVWASI_ENOMEM;
128       goto exit;
129     }
130 
131     for (i = table->size; i < new_size; ++i)
132       new_fds[i] = NULL;
133 
134     index = table->size;
135     table->fds = new_fds;
136     table->size = new_size;
137   } else {
138     /* The table is big enough, so find an empty slot for the new data. */
139     int valid_slot = 0;
140     for (i = 0; i < table->size; ++i) {
141       if (table->fds[i] == NULL) {
142         valid_slot = 1;
143         index = i;
144         break;
145       }
146     }
147 
148     /* This should never happen. */
149     if (valid_slot == 0) {
150       uvwasi__free(uvwasi, entry);
151       err = UVWASI_ENOSPC;
152       goto exit;
153     }
154   }
155 
156   table->fds[index] = entry;
157 
158   r = uv_mutex_init(&entry->mutex);
159   if (r != 0) {
160     err = uvwasi__translate_uv_error(r);
161     goto exit;
162   }
163 
164   entry->id = index;
165   entry->fd = fd;
166   entry->sock = sock;
167   entry->path = mp_copy;
168   entry->real_path = rp_copy;
169   entry->normalized_path = np_copy;
170   entry->type = type;
171   entry->rights_base = rights_base;
172   entry->rights_inheriting = rights_inheriting;
173   entry->preopen = preopen;
174 
175   if (wrap != NULL) {
176     uv_mutex_lock(&entry->mutex);
177     *wrap = entry;
178   }
179 
180   table->used++;
181   err = UVWASI_ESUCCESS;
182 exit:
183   uv_rwlock_wrunlock(&table->rwlock);
184   return err;
185 }
186 
187 
uvwasi_fd_table_init(uvwasi_t * uvwasi,const uvwasi_options_t * options)188 uvwasi_errno_t uvwasi_fd_table_init(uvwasi_t* uvwasi,
189                                     const uvwasi_options_t* options) {
190   struct uvwasi_fd_table_t* table;
191   uvwasi_errno_t err;
192   int r;
193 
194   /* Require an initial size of at least three to store the stdio FDs. */
195   if (uvwasi == NULL || options == NULL || options->fd_table_size < 3)
196     return UVWASI_EINVAL;
197 
198   table = uvwasi__malloc(uvwasi, sizeof(*table));
199   if (table == NULL)
200     return UVWASI_ENOMEM;
201 
202   table->used = 0;
203   table->size = options->fd_table_size;
204   table->fds = uvwasi__calloc(uvwasi,
205                               options->fd_table_size,
206                               sizeof(struct uvwasi_fd_wrap_t*));
207   if (table->fds == NULL) {
208     uvwasi__free(uvwasi, table);
209     return UVWASI_ENOMEM;
210   }
211 
212   r = uv_rwlock_init(&table->rwlock);
213   if (r != 0) {
214     err = uvwasi__translate_uv_error(r);
215     uvwasi__free(uvwasi, table->fds);
216     uvwasi__free(uvwasi, table);
217     return err;
218   }
219 
220   /* Create the stdio FDs. */
221   err = uvwasi__insert_stdio(uvwasi, table, options->in, 0, "<stdin>");
222   if (err != UVWASI_ESUCCESS)
223     goto error_exit;
224 
225   err = uvwasi__insert_stdio(uvwasi, table, options->out, 1, "<stdout>");
226   if (err != UVWASI_ESUCCESS)
227     goto error_exit;
228 
229   err = uvwasi__insert_stdio(uvwasi, table, options->err, 2, "<stderr>");
230   if (err != UVWASI_ESUCCESS)
231     goto error_exit;
232 
233   uvwasi->fds = table;
234   return UVWASI_ESUCCESS;
235 error_exit:
236   uvwasi_fd_table_free(uvwasi, table);
237   return err;
238 }
239 
240 
uvwasi_fd_table_free(uvwasi_t * uvwasi,struct uvwasi_fd_table_t * table)241 void uvwasi_fd_table_free(uvwasi_t* uvwasi, struct uvwasi_fd_table_t* table) {
242   struct uvwasi_fd_wrap_t* entry;
243   uint32_t i;
244 
245   if (uvwasi == NULL || table == NULL)
246     return;
247 
248   for (i = 0; i < table->size; i++) {
249     entry = table->fds[i];
250 
251     if (entry == NULL)
252       continue;
253 
254     uv_mutex_destroy(&entry->mutex);
255     uvwasi__free(uvwasi, entry);
256   }
257 
258   if (table->fds != NULL) {
259     uvwasi__free(uvwasi, table->fds);
260     table->fds = NULL;
261     table->size = 0;
262     table->used = 0;
263     uv_rwlock_destroy(&table->rwlock);
264   }
265 
266   uvwasi__free(uvwasi, table);
267 }
268 
269 
uvwasi_fd_table_insert_preopen(uvwasi_t * uvwasi,struct uvwasi_fd_table_t * table,const uv_file fd,const char * path,const char * real_path)270 uvwasi_errno_t uvwasi_fd_table_insert_preopen(uvwasi_t* uvwasi,
271                                               struct uvwasi_fd_table_t* table,
272                                               const uv_file fd,
273                                               const char* path,
274                                               const char* real_path) {
275   uvwasi_filetype_t type;
276   uvwasi_rights_t base;
277   uvwasi_rights_t inheriting;
278   uvwasi_errno_t err;
279 
280   if (table == NULL || path == NULL || real_path == NULL)
281     return UVWASI_EINVAL;
282 
283   err = uvwasi__get_filetype_by_fd(fd, &type);
284   if (err != UVWASI_ESUCCESS)
285     return err;
286 
287   if (type != UVWASI_FILETYPE_DIRECTORY)
288     return UVWASI_ENOTDIR;
289 
290   err = uvwasi__get_rights(fd, 0, type, &base, &inheriting);
291   if (err != UVWASI_ESUCCESS)
292     return err;
293 
294   return uvwasi_fd_table_insert(uvwasi,
295                                 table,
296                                 fd,
297                                 NULL,
298                                 path,
299                                 real_path,
300                                 UVWASI_FILETYPE_DIRECTORY,
301                                 UVWASI__RIGHTS_DIRECTORY_BASE,
302                                 UVWASI__RIGHTS_DIRECTORY_INHERITING,
303                                 1,
304                                 NULL);
305 }
306 
307 
uvwasi_fd_table_insert_preopen_socket(uvwasi_t * uvwasi,struct uvwasi_fd_table_t * table,uv_tcp_t * sock)308 uvwasi_errno_t uvwasi_fd_table_insert_preopen_socket(uvwasi_t* uvwasi,
309                                               struct uvwasi_fd_table_t* table,
310                                               uv_tcp_t* sock) {
311   if (table == NULL || sock == NULL)
312     return UVWASI_EINVAL;
313 
314   return uvwasi_fd_table_insert(uvwasi,
315                                 table,
316                                 -1,
317                                 sock,
318                                 NULL,
319                                 NULL,
320                                 UVWASI_FILETYPE_SOCKET_STREAM,
321                                 UVWASI__RIGHTS_SOCKET_BASE,
322                                 UVWASI__RIGHTS_SOCKET_INHERITING,
323                                 1,
324                                 NULL);
325 }
326 
327 
uvwasi_fd_table_get(struct uvwasi_fd_table_t * table,const uvwasi_fd_t id,struct uvwasi_fd_wrap_t ** wrap,uvwasi_rights_t rights_base,uvwasi_rights_t rights_inheriting)328 uvwasi_errno_t uvwasi_fd_table_get(struct uvwasi_fd_table_t* table,
329                                    const uvwasi_fd_t id,
330                                    struct uvwasi_fd_wrap_t** wrap,
331                                    uvwasi_rights_t rights_base,
332                                    uvwasi_rights_t rights_inheriting) {
333   uvwasi_errno_t err;
334 
335   if (table == NULL)
336     return UVWASI_EINVAL;
337 
338   uv_rwlock_wrlock(&table->rwlock);
339   err = uvwasi_fd_table_get_nolock(table,
340                                    id,
341                                    wrap,
342                                    rights_base,
343                                    rights_inheriting);
344   uv_rwlock_wrunlock(&table->rwlock);
345   return err;
346 }
347 
348 
349 /* uvwasi_fd_table_get_nolock() retrieves a file descriptor and locks its mutex,
350    but does not lock the file descriptor table like uvwasi_fd_table_get() does.
351 */
uvwasi_fd_table_get_nolock(struct uvwasi_fd_table_t * table,const uvwasi_fd_t id,struct uvwasi_fd_wrap_t ** wrap,uvwasi_rights_t rights_base,uvwasi_rights_t rights_inheriting)352 uvwasi_errno_t uvwasi_fd_table_get_nolock(struct uvwasi_fd_table_t* table,
353                                           const uvwasi_fd_t id,
354                                           struct uvwasi_fd_wrap_t** wrap,
355                                           uvwasi_rights_t rights_base,
356                                           uvwasi_rights_t rights_inheriting) {
357   struct uvwasi_fd_wrap_t* entry;
358 
359   if (table == NULL || wrap == NULL)
360     return UVWASI_EINVAL;
361 
362   if (id >= table->size)
363     return UVWASI_EBADF;
364 
365   entry = table->fds[id];
366 
367   if (entry == NULL || entry->id != id)
368     return UVWASI_EBADF;
369 
370   /* Validate that the fd has the necessary rights. */
371   if ((~entry->rights_base & rights_base) != 0 ||
372       (~entry->rights_inheriting & rights_inheriting) != 0) {
373     return UVWASI_ENOTCAPABLE;
374   }
375 
376   uv_mutex_lock(&entry->mutex);
377   *wrap = entry;
378   return UVWASI_ESUCCESS;
379 }
380 
381 
uvwasi_fd_table_remove_nolock(uvwasi_t * uvwasi,struct uvwasi_fd_table_t * table,const uvwasi_fd_t id)382 uvwasi_errno_t uvwasi_fd_table_remove_nolock(uvwasi_t* uvwasi,
383                                              struct uvwasi_fd_table_t* table,
384                                              const uvwasi_fd_t id) {
385   struct uvwasi_fd_wrap_t* entry;
386 
387   if (table == NULL)
388     return UVWASI_EINVAL;
389 
390   if (id >= table->size)
391     return UVWASI_EBADF;
392 
393   entry = table->fds[id];
394 
395   if (entry == NULL || entry->id != id)
396     return UVWASI_EBADF;
397 
398   uv_mutex_destroy(&entry->mutex);
399   uvwasi__free(uvwasi, entry);
400   table->fds[id] = NULL;
401   table->used--;
402   return UVWASI_ESUCCESS;
403 }
404 
405 
uvwasi_fd_table_renumber(struct uvwasi_s * uvwasi,struct uvwasi_fd_table_t * table,const uvwasi_fd_t dst,const uvwasi_fd_t src)406 uvwasi_errno_t uvwasi_fd_table_renumber(struct uvwasi_s* uvwasi,
407                                         struct uvwasi_fd_table_t* table,
408                                         const uvwasi_fd_t dst,
409                                         const uvwasi_fd_t src) {
410   struct uvwasi_fd_wrap_t* dst_entry;
411   struct uvwasi_fd_wrap_t* src_entry;
412   uv_fs_t req;
413   uvwasi_errno_t err;
414   int r;
415 
416   if (uvwasi == NULL || table == NULL)
417     return UVWASI_EINVAL;
418 
419   if (dst == src)
420     return UVWASI_ESUCCESS;
421 
422   uv_rwlock_wrlock(&table->rwlock);
423 
424   if (dst >= table->size || src >= table->size) {
425     err = UVWASI_EBADF;
426     goto exit;
427   }
428 
429   dst_entry = table->fds[dst];
430   src_entry = table->fds[src];
431 
432   if (dst_entry == NULL || dst_entry->id != dst ||
433       src_entry == NULL || src_entry->id != src) {
434     err = UVWASI_EBADF;
435     goto exit;
436   }
437 
438   uv_mutex_lock(&dst_entry->mutex);
439   uv_mutex_lock(&src_entry->mutex);
440 
441   /* Close the existing destination descriptor. */
442   r = uv_fs_close(NULL, &req, dst_entry->fd, NULL);
443   uv_fs_req_cleanup(&req);
444   if (r != 0) {
445     uv_mutex_unlock(&src_entry->mutex);
446     uv_mutex_unlock(&dst_entry->mutex);
447     err = uvwasi__translate_uv_error(r);
448     goto exit;
449   }
450 
451   /* Move the source entry to the destination slot in the table. */
452   table->fds[dst] = table->fds[src];
453   table->fds[dst]->id = dst;
454   uv_mutex_unlock(&table->fds[dst]->mutex);
455   table->fds[src] = NULL;
456   table->used--;
457 
458   /* Clean up what's left of the old destination entry. */
459   uv_mutex_unlock(&dst_entry->mutex);
460   uv_mutex_destroy(&dst_entry->mutex);
461   uvwasi__free(uvwasi, dst_entry);
462 
463   err = UVWASI_ESUCCESS;
464 exit:
465   uv_rwlock_wrunlock(&table->rwlock);
466   return err;
467 }
468 
469 
uvwasi_fd_table_lock(struct uvwasi_fd_table_t * table)470 uvwasi_errno_t uvwasi_fd_table_lock(struct uvwasi_fd_table_t* table) {
471   if (table == NULL)
472     return UVWASI_EINVAL;
473 
474   uv_rwlock_wrlock(&table->rwlock);
475   return UVWASI_ESUCCESS;
476 }
477 
478 
uvwasi_fd_table_unlock(struct uvwasi_fd_table_t * table)479 uvwasi_errno_t uvwasi_fd_table_unlock(struct uvwasi_fd_table_t* table) {
480   if (table == NULL)
481     return UVWASI_EINVAL;
482 
483   uv_rwlock_wrunlock(&table->rwlock);
484   return UVWASI_ESUCCESS;
485 }
486