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