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