Coverage Report

Created: 2017-04-15 07:07

/home/travis/build/MoarVM/MoarVM/src/io/syncfile.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
#include "platform/io.h"
3
4
/* Here we implement synchronous file I/O. It's done using libuv's file I/O
5
 * functions, without specifying callbacks, thus easily giving synchronous
6
 * behavior. */
7
8
#ifndef _WIN32
9
#include <sys/types.h>
10
#include <unistd.h>
11
196
#define DEFAULT_MODE 0x01B6
12
#else
13
#include <fcntl.h>
14
#define O_CREAT  _O_CREAT
15
#define O_RDONLY _O_RDONLY
16
#define O_WRONLY _O_WRONLY
17
#define O_TRUNC  _O_TRUNC
18
#define O_EXCL   _O_EXCL
19
#define O_RDWR   _O_RDWR
20
#define DEFAULT_MODE _S_IWRITE /* work around sucky libuv defaults */
21
#endif
22
23
/* Number of bytes we pull in at a time to the buffer. */
24
21
#define CHUNK_SIZE 32768
25
26
/* Data that we keep for a file-based handle. */
27
typedef struct {
28
    /* libuv file descriptor. */
29
    uv_file fd;
30
31
    /* The filename we opened, as a C string. */
32
    char *filename;
33
34
    /* The encoding we're using. */
35
    MVMint64 encoding;
36
37
    /* Decode stream, for turning bytes from disk into strings. */
38
    MVMDecodeStream *ds;
39
40
    /* Current separator specification for line-by-line reading. */
41
    MVMDecodeStreamSeparators sep_spec;
42
} MVMIOFileData;
43
44
/* Closes the file. */
45
194
static MVMint64 closefh(MVMThreadContext *tc, MVMOSHandle *h) {
46
194
    MVMIOFileData *data = (MVMIOFileData *)h->body.data;
47
194
    uv_fs_t req;
48
194
    if (data->ds) {
49
176
        MVM_string_decodestream_destroy(tc, data->ds);
50
176
        data->ds = NULL;
51
176
    }
52
194
    if (uv_fs_close(tc->loop, &req, data->fd, NULL) < 0) {
53
0
        data->fd = -1;
54
0
        MVM_exception_throw_adhoc(tc, "Failed to close filehandle: %s", uv_strerror(req.result));
55
0
    }
56
194
    data->fd = -1;
57
194
    return 0;
58
194
}
59
60
/* Checks if the file is a TTY. */
61
1
static MVMint64 is_tty(MVMThreadContext *tc, MVMOSHandle *h) {
62
1
    MVMIOFileData *data = (MVMIOFileData *)h->body.data;
63
1
    return (uv_guess_handle(data->fd) == UV_TTY);
64
1
}
65
66
/* Gets the file descriptor. */
67
0
static MVMint64 mvm_fileno(MVMThreadContext *tc, MVMOSHandle *h) {
68
0
    MVMIOFileData *data = (MVMIOFileData *)h->body.data;
69
0
    return (MVMint64)data->fd;
70
0
}
71
72
/* Sets the encoding used for string-based I/O. */
73
157
static void set_encoding(MVMThreadContext *tc, MVMOSHandle *h, MVMint64 encoding) {
74
157
    MVMIOFileData *data = (MVMIOFileData *)h->body.data;
75
157
    if (data->ds)
76
0
        MVM_exception_throw_adhoc(tc, "Too late to change handle encoding");
77
157
    data->encoding = encoding;
78
157
}
79
80
/* Seek to the specified position in the file. */
81
6
static void seek(MVMThreadContext *tc, MVMOSHandle *h, MVMint64 offset, MVMint64 whence) {
82
6
    MVMIOFileData *data = (MVMIOFileData *)h->body.data;
83
6
    MVMint64 r;
84
6
85
6
    if (data->ds) {
86
4
        /* We'll start over from a new position. */
87
4
        MVM_string_decodestream_destroy(tc, data->ds);
88
4
        data->ds = NULL;
89
4
    }
90
6
91
6
    /* Seek, then get absolute position for new decodestream. */
92
6
    if (MVM_platform_lseek(data->fd, offset, whence) == -1)
93
2
        MVM_exception_throw_adhoc(tc, "Failed to seek in filehandle: %d", errno);
94
6
    if ((r = MVM_platform_lseek(data->fd, 0, SEEK_CUR)) == -1)
95
0
        MVM_exception_throw_adhoc(tc, "Failed to seek in filehandle: %d", errno);
96
6
    data->ds = MVM_string_decodestream_create(tc, data->encoding, r, 1);
97
6
}
98
99
/* Get curernt position in the file. */
100
9
static MVMint64 mvm_tell(MVMThreadContext *tc, MVMOSHandle *h) {
101
9
    MVMIOFileData *data = (MVMIOFileData *)h->body.data;
102
9
    MVMint64 r;
103
9
104
9
    if (data->ds)
105
8
        return MVM_string_decodestream_tell_bytes(tc, data->ds);
106
9
107
1
    if ((r = MVM_platform_lseek(data->fd, 0, SEEK_CUR)) == -1)
108
0
        MVM_exception_throw_adhoc(tc, "Failed to tell in filehandle: %d", errno);
109
1
110
1
    return r;
111
9
}
112
113
/* Set the line separator. */
114
7
static void set_separator(MVMThreadContext *tc, MVMOSHandle *h, MVMString **seps, MVMint32 num_seps) {
115
7
    MVMIOFileData *data = (MVMIOFileData *)h->body.data;
116
7
    MVM_string_decode_stream_sep_from_strings(tc, &(data->sep_spec), seps, num_seps);
117
7
}
118
119
/* Read a bunch of bytes into the current decode stream. */
120
361
static MVMint32 read_to_buffer(MVMThreadContext *tc, MVMIOFileData *data, MVMint32 bytes) {
121
361
    char *buf         = MVM_malloc(bytes);
122
361
    uv_buf_t read_buf = uv_buf_init(buf, bytes);
123
361
    uv_fs_t req;
124
361
    MVMint32 read;
125
361
    MVM_gc_mark_thread_blocked(tc);
126
361
    if ((read = uv_fs_read(tc->loop, &req, data->fd, &read_buf, 1, -1, NULL)) < 0) {
127
0
        MVM_free(buf);
128
0
        MVM_gc_mark_thread_unblocked(tc);
129
0
        MVM_exception_throw_adhoc(tc, "Reading from filehandle failed: %s",
130
0
            uv_strerror(req.result));
131
0
    }
132
361
    MVM_string_decodestream_add_bytes(tc, data->ds, buf, read);
133
361
    MVM_gc_mark_thread_unblocked(tc);
134
361
    return read;
135
361
}
136
137
/* Ensures we have a decode stream, creating it if we're missing one. */
138
204
static void ensure_decode_stream(MVMThreadContext *tc, MVMIOFileData *data) {
139
204
    if (!data->ds)
140
177
        data->ds = MVM_string_decodestream_create(tc, data->encoding, 0, 1);
141
204
}
142
143
/* Reads a single line from the file handle. May serve it from a buffer, if we
144
 * already read enough data. */
145
26
static MVMString * read_line(MVMThreadContext *tc, MVMOSHandle *h, MVMint32 chomp) {
146
26
    MVMIOFileData *data = (MVMIOFileData *)h->body.data;
147
26
    ensure_decode_stream(tc, data);
148
26
149
26
    /* Pull data until we can read a line. */
150
35
    do {
151
35
        MVMString *line = MVM_string_decodestream_get_until_sep(tc,
152
35
            data->ds, &(data->sep_spec), chomp);
153
35
        if (line != NULL)
154
16
            return line;
155
19
    } while (read_to_buffer(tc, data, CHUNK_SIZE) > 0);
156
26
157
26
    /* Reached end of file, or last (non-termianted) line. */
158
10
    return MVM_string_decodestream_get_until_sep_eof(tc, data->ds,
159
10
        &(data->sep_spec), chomp);
160
26
}
161
162
/* Reads the file from the current position to the end into a string. */
163
170
static MVMString * slurp(MVMThreadContext *tc, MVMOSHandle *h) {
164
170
    MVMIOFileData *data = (MVMIOFileData *)h->body.data;
165
170
    uv_fs_t req;
166
170
    ensure_decode_stream(tc, data);
167
170
168
170
    /* Typically we're slurping an entire file, so just request the bytes
169
170
     * until the end; repeat to ensure we get 'em all. */
170
170
    if (uv_fs_fstat(tc->loop, &req, data->fd, NULL) < 0) {
171
0
        MVM_exception_throw_adhoc(tc, "slurp from filehandle failed: %s", uv_strerror(req.result));
172
0
    }
173
170
    /* Sometimes - usually for special files like those in /proc - the file
174
170
     * size comes up 0, even though the S_ISREG test succeeds. So in that case
175
170
     * we try a small read and switch to a "read chunks until EOF" impl.
176
170
     * Otherwise we just read the exact size of the file. */
177
170
    if (req.statbuf.st_size == 0) {
178
2
        if (read_to_buffer(tc, data, 32) > 0) {
179
0
            while (read_to_buffer(tc, data, 4096) > 0)
180
0
                ;
181
0
        }
182
168
    } else {
183
334
        while (read_to_buffer(tc, data, req.statbuf.st_size) > 0)
184
166
            ;
185
168
    }
186
170
    return MVM_string_decodestream_get_all(tc, data->ds);
187
170
}
188
189
/* Gets the specified number of characters from the file. */
190
4
static MVMString * read_chars(MVMThreadContext *tc, MVMOSHandle *h, MVMint64 chars) {
191
4
    MVMIOFileData *data = (MVMIOFileData *)h->body.data;
192
4
    ensure_decode_stream(tc, data);
193
4
194
4
    /* Pull data until we can read the chars we want. */
195
5
    do {
196
5
        MVMString *result = MVM_string_decodestream_get_chars(tc, data->ds, chars);
197
5
        if (result != NULL)
198
3
            return result;
199
2
    } while (read_to_buffer(tc, data, CHUNK_SIZE) > 0);
200
4
201
4
    /* Reached end of file, so just take what we have. */
202
1
    return MVM_string_decodestream_get_all(tc, data->ds);
203
4
}
204
205
/* Reads the specified number of bytes into a the supplied buffer, returing
206
 * the number actually read. */
207
4
static MVMint64 read_bytes(MVMThreadContext *tc, MVMOSHandle *h, char **buf, MVMint64 bytes) {
208
4
    MVMIOFileData *data = (MVMIOFileData *)h->body.data;
209
4
    ensure_decode_stream(tc, data);
210
4
211
4
    /* Keep requesting bytes until we have enough in the buffer or we hit
212
4
     * end of file. */
213
8
    while (!MVM_string_decodestream_have_bytes(tc, data->ds, bytes)) {
214
4
        if (read_to_buffer(tc, data, bytes) <= 0)
215
0
            break;
216
4
    }
217
4
218
4
    /* Read as many as we can, up to the limit. */
219
4
    return MVM_string_decodestream_bytes_to_buf(tc, data->ds, buf, bytes);
220
4
}
221
222
/* Checks if the end of file has been reached. */
223
3
static MVMint64 mvm_eof(MVMThreadContext *tc, MVMOSHandle *h) {
224
3
    MVMIOFileData *data = (MVMIOFileData *)h->body.data;
225
3
    MVMint64 seek_pos;
226
3
    uv_fs_t  req;
227
3
    if (data->ds && !MVM_string_decodestream_is_empty(tc, data->ds))
228
1
        return 0;
229
2
    if (uv_fs_fstat(tc->loop, &req, data->fd, NULL) == -1) {
230
0
        MVM_exception_throw_adhoc(tc, "Failed to stat file descriptor: %s", uv_strerror(req.result));
231
0
    }
232
2
    if ((seek_pos = MVM_platform_lseek(data->fd, 0, SEEK_CUR)) == -1)
233
0
        MVM_exception_throw_adhoc(tc, "Failed to seek in filehandle: %d", errno);
234
2
    /* Comparison with seek_pos for some special files, like those in /proc,
235
2
     * which file size is 0 can be false. In that case, we fall back to check
236
2
     * file size to detect EOF. */
237
1
    return req.statbuf.st_size == seek_pos || req.statbuf.st_size == 0;
238
3
}
239
240
/* Writes the specified string to the file handle, maybe with a newline. */
241
19
static MVMint64 write_str(MVMThreadContext *tc, MVMOSHandle *h, MVMString *str, MVMint64 newline) {
242
19
    MVMIOFileData *data = (MVMIOFileData *)h->body.data;
243
19
    MVMuint64 output_size;
244
19
    MVMint64 bytes_written;
245
19
    char *output = MVM_string_encode(tc, str, 0, -1, &output_size, data->encoding, NULL,
246
19
        MVM_TRANSLATE_NEWLINE_OUTPUT);
247
19
    uv_buf_t write_buf  = uv_buf_init(output, output_size);
248
19
    uv_fs_t req;
249
19
250
19
    bytes_written = uv_fs_write(tc->loop, &req, data->fd, &write_buf, 1, -1, NULL);
251
19
    if (bytes_written < 0) {
252
0
        MVM_free(output);
253
0
        MVM_exception_throw_adhoc(tc, "Failed to write bytes to filehandle: %s", uv_strerror(req.result));
254
0
    }
255
19
    MVM_free(output);
256
19
257
19
    if (newline) {
258
0
        uv_buf_t nl = uv_buf_init("\n", 1);
259
0
        if (uv_fs_write(tc->loop, &req, data->fd, &nl, 1, -1, NULL) < 0)
260
0
            MVM_exception_throw_adhoc(tc, "Failed to write newline to filehandle: %s", uv_strerror(req.result));
261
0
        bytes_written++;
262
0
    }
263
19
264
19
    return bytes_written;
265
19
}
266
267
/* Writes the specified bytes to the file handle. */
268
2
static MVMint64 write_bytes(MVMThreadContext *tc, MVMOSHandle *h, char *buf, MVMint64 bytes) {
269
2
    MVMIOFileData *data = (MVMIOFileData *)h->body.data;
270
2
    uv_buf_t write_buf  = uv_buf_init(buf, bytes);
271
2
    uv_fs_t  req;
272
2
    MVMint64 bytes_written;
273
2
    bytes_written = uv_fs_write(tc->loop, &req, data->fd, &write_buf, 1, -1, NULL);
274
2
    if (bytes_written < 0)
275
0
        MVM_exception_throw_adhoc(tc, "Failed to write bytes to filehandle: %s", uv_strerror(req.result));
276
2
    return bytes_written;
277
2
}
278
279
/* Flushes the file handle. */
280
0
static void flush(MVMThreadContext *tc, MVMOSHandle *h){
281
0
    MVMIOFileData *data = (MVMIOFileData *)h->body.data;
282
0
    uv_fs_t req;
283
0
    if (uv_fs_fsync(tc->loop, &req, data->fd, NULL) < 0 )
284
0
        MVM_exception_throw_adhoc(tc, "Failed to flush filehandle: %s", uv_strerror(req.result));
285
0
}
286
287
/* Truncates the file handle. */
288
0
static void truncatefh(MVMThreadContext *tc, MVMOSHandle *h, MVMint64 bytes) {
289
0
    MVMIOFileData *data = (MVMIOFileData *)h->body.data;
290
0
    uv_fs_t req;
291
0
    if(uv_fs_ftruncate(tc->loop, &req, data->fd, bytes, NULL) < 0 )
292
0
        MVM_exception_throw_adhoc(tc, "Failed to truncate filehandle: %s", uv_strerror(req.result));
293
0
}
294
295
/* Operations aiding process spawning and I/O handling. */
296
static void bind_stdio_handle(MVMThreadContext *tc, MVMOSHandle *h, uv_stdio_container_t *stdio,
297
0
        uv_process_t *process) {
298
0
    MVMIOFileData *data = (MVMIOFileData *)h->body.data;
299
0
    stdio->flags        = UV_INHERIT_FD;
300
0
    stdio->data.fd      = data->fd;
301
0
}
302
303
/* Locks a file. */
304
0
static MVMint64 lock(MVMThreadContext *tc, MVMOSHandle *h, MVMint64 flag) {
305
0
    MVMIOFileData *data = (MVMIOFileData *)h->body.data;
306
0
307
0
#ifdef _WIN32
308
309
    const DWORD len = 0xffffffff;
310
    const HANDLE hf = (HANDLE)_get_osfhandle(data->fd);
311
    OVERLAPPED offset;
312
313
    if (hf == INVALID_HANDLE_VALUE) {
314
        MVM_exception_throw_adhoc(tc, "Failed to lock filehandle: bad file descriptor");
315
    }
316
317
    flag = ((flag & MVM_FILE_FLOCK_NONBLOCK) ? LOCKFILE_FAIL_IMMEDIATELY : 0)
318
          + ((flag & MVM_FILE_FLOCK_TYPEMASK) == MVM_FILE_FLOCK_SHARED
319
                                       ? 0 : LOCKFILE_EXCLUSIVE_LOCK);
320
321
    memset (&offset, 0, sizeof(offset));
322
    if (LockFileEx(hf, flag, 0, len, len, &offset)) {
323
        return 1;
324
    }
325
326
    MVM_exception_throw_adhoc(tc, "Failed to lock filehandle: %d", GetLastError());
327
328
    return 0;
329
330
#else
331
0
332
0
    struct flock l;
333
0
    ssize_t r;
334
0
    int fc;
335
0
    const int fd = data->fd;
336
0
337
0
    l.l_whence = SEEK_SET;
338
0
    l.l_start = 0;
339
0
    l.l_len = 0;
340
0
341
0
    if ((flag & MVM_FILE_FLOCK_TYPEMASK) == MVM_FILE_FLOCK_SHARED)
342
0
        l.l_type = F_RDLCK;
343
0
    else
344
0
        l.l_type = F_WRLCK;
345
0
346
0
    fc = (flag & MVM_FILE_FLOCK_NONBLOCK) ? F_SETLK : F_SETLKW;
347
0
348
0
    do {
349
0
        r = fcntl(fd, fc, &l);
350
0
    } while (r == -1 && errno == EINTR);
351
0
352
0
    if (r == -1) {
353
0
        MVM_exception_throw_adhoc(tc, "Failed to lock filehandle: %d", errno);
354
0
    }
355
0
356
0
    return 1;
357
0
#endif
358
0
}
359
360
/* Unlocks a file. */
361
0
static void unlock(MVMThreadContext *tc, MVMOSHandle *h) {
362
0
    MVMIOFileData *data = (MVMIOFileData *)h->body.data;
363
0
364
0
#ifdef _WIN32
365
366
    const DWORD len = 0xffffffff;
367
    const HANDLE hf = (HANDLE)_get_osfhandle(data->fd);
368
    OVERLAPPED offset;
369
370
    if (hf == INVALID_HANDLE_VALUE) {
371
        MVM_exception_throw_adhoc(tc, "Failed to seek in filehandle: bad file descriptor");
372
    }
373
374
    memset (&offset, 0, sizeof(offset));
375
    if (UnlockFileEx(hf, 0, len, len, &offset)) {
376
        return;
377
    }
378
379
    MVM_exception_throw_adhoc(tc, "Failed to unlock filehandle: %d", GetLastError());
380
#else
381
0
382
0
    struct flock l;
383
0
    ssize_t r;
384
0
    const int fd = data->fd;
385
0
386
0
    l.l_whence = SEEK_SET;
387
0
    l.l_start = 0;
388
0
    l.l_len = 0;
389
0
    l.l_type = F_UNLCK;
390
0
391
0
    do {
392
0
        r = fcntl(fd, F_SETLKW, &l);
393
0
    } while (r == -1 && errno == EINTR);
394
0
395
0
    if (r == -1) {
396
0
        MVM_exception_throw_adhoc(tc, "Failed to unlock filehandle: %d", errno);
397
0
    }
398
0
#endif
399
0
}
400
401
/* Frees data associated with the handle. */
402
52
static void gc_free(MVMThreadContext *tc, MVMObject *h, void *d) {
403
52
    MVMIOFileData *data = (MVMIOFileData *)d;
404
52
    if (data) {
405
52
        if (data->ds)
406
0
            MVM_string_decodestream_destroy(tc, data->ds);
407
52
        MVM_string_decode_stream_sep_destroy(tc, &(data->sep_spec));
408
52
        if (data->filename)
409
52
            MVM_free(data->filename);
410
52
        MVM_free(data);
411
52
    }
412
52
}
413
414
/* IO ops table, populated with functions. */
415
static const MVMIOClosable      closable      = { closefh };
416
static const MVMIOEncodable     encodable     = { set_encoding };
417
static const MVMIOSyncReadable  sync_readable = { set_separator, read_line, slurp, read_chars, read_bytes, mvm_eof };
418
static const MVMIOSyncWritable  sync_writable = { write_str, write_bytes, flush, truncatefh };
419
static const MVMIOSeekable      seekable      = { seek, mvm_tell };
420
static const MVMIOPipeable      pipeable      = { bind_stdio_handle };
421
static const MVMIOLockable      lockable      = { lock, unlock };
422
static const MVMIOIntrospection introspection = { is_tty, mvm_fileno };
423
424
static const MVMIOOps op_table = {
425
    &closable,
426
    &encodable,
427
    &sync_readable,
428
    &sync_writable,
429
    NULL,
430
    NULL,
431
    NULL,
432
    &seekable,
433
    NULL,
434
    &pipeable,
435
    &lockable,
436
    &introspection,
437
    NULL,
438
    gc_free
439
};
440
441
/* Builds POSIX flag from mode string. */
442
196
static int resolve_open_mode(int *flag, const char *cp) {
443
196
    switch (*cp++) {
444
179
        case 'r': *flag = O_RDONLY; break;
445
0
        case '-': *flag = O_WRONLY; break;
446
0
        case '+': *flag = O_RDWR;   break;
447
179
448
179
        /* alias for "-c" or "-ct" if by itself */
449
17
        case 'w':
450
16
        *flag = *cp ? O_WRONLY | O_CREAT : O_WRONLY | O_CREAT | O_TRUNC;
451
17
        break;
452
179
453
0
        default:
454
0
        return 0;
455
196
    }
456
196
457
197
    for (;;) switch (*cp++) {
458
196
        case 0:
459
196
        return 1;
460
196
461
1
        case 'a': *flag |= O_APPEND; break;
462
0
        case 'c': *flag |= O_CREAT;  break;
463
0
        case 't': *flag |= O_TRUNC;  break;
464
0
        case 'x': *flag |= O_EXCL;   break;
465
196
466
0
        default:
467
0
        return 0;
468
197
    }
469
196
}
470
471
/* Opens a file, returning a synchronous file handle. */
472
196
MVMObject * MVM_file_open_fh(MVMThreadContext *tc, MVMString *filename, MVMString *mode) {
473
196
    char * const fname = MVM_string_utf8_c8_encode_C_string(tc, filename);
474
196
    uv_fs_t req;
475
196
    uv_file fd;
476
196
    int flag;
477
196
478
196
    /* Resolve mode description to flags. */
479
196
    {
480
196
        char * const fmode  = MVM_string_utf8_encode_C_string(tc, mode);
481
196
482
196
        if (!resolve_open_mode(&flag, fmode)) {
483
0
            char *waste[] = { fname, fmode, NULL };
484
0
            MVM_exception_throw_adhoc_free(tc, waste, "Invalid open mode for file %s: %s", fname, fmode);
485
0
        }
486
196
        MVM_free(fmode);
487
196
    }
488
196
489
196
    /* Try to open the file. */
490
196
    if ((fd = uv_fs_open(tc->loop, &req, (const char *)fname, flag, DEFAULT_MODE, NULL)) < 0) {
491
0
        char *waste[] = { fname, NULL };
492
0
        const char *err = uv_strerror(req.result);
493
0
494
0
        uv_fs_req_cleanup(&req);
495
0
        MVM_exception_throw_adhoc_free(tc, waste, "Failed to open file %s: %s", fname, err);
496
0
    }
497
196
    uv_fs_req_cleanup(&req);
498
196
499
196
    /*  Check that we didn't open a directory by accident.
500
196
        If fstat fails, just move on: Most of the documented error cases should
501
196
        already have triggered when opening the file, and we can't do anything
502
196
        about the others; a failure also does not necessarily imply that the
503
196
        file descriptor cannot be used for reading/writing. */
504
196
    if (uv_fs_fstat(tc->loop, &req, fd, NULL) == 0 && (req.statbuf.st_mode & S_IFMT) == S_IFDIR) {
505
0
        char *waste[] = { fname, NULL };
506
0
507
0
        uv_fs_req_cleanup(&req);
508
0
509
0
        if (uv_fs_close(tc->loop, &req, fd, NULL) < 0) {
510
0
            const char *err = uv_strerror(req.result);
511
0
512
0
            uv_fs_req_cleanup(&req);
513
0
            MVM_exception_throw_adhoc_free(tc, waste, "Tried to open directory %s, which we failed to close: %s",
514
0
                fname, err);
515
0
        }
516
0
        uv_fs_req_cleanup(&req);
517
0
518
0
        MVM_exception_throw_adhoc_free(tc, waste, "Tried to open directory %s", fname);
519
0
    }
520
196
    uv_fs_req_cleanup(&req);
521
196
522
196
    /* Set up handle. */
523
196
    {
524
196
        MVMIOFileData * const data   = MVM_calloc(1, sizeof(MVMIOFileData));
525
196
        MVMOSHandle   * const result = (MVMOSHandle *)MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTIO);
526
196
527
196
        data->fd          = fd;
528
196
        data->filename    = fname;
529
196
        data->encoding    = MVM_encoding_type_utf8;
530
196
        MVM_string_decode_stream_sep_default(tc, &(data->sep_spec));
531
196
        result->body.ops  = &op_table;
532
196
        result->body.data = data;
533
196
534
196
        return (MVMObject *)result;
535
196
    }
536
196
}
537
538
/* Opens a file, returning a synchronous file handle. */
539
0
MVMObject * MVM_file_handle_from_fd(MVMThreadContext *tc, uv_file fd) {
540
0
    MVMOSHandle   * const result = (MVMOSHandle *)MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTIO);
541
0
    MVMIOFileData * const data   = MVM_calloc(1, sizeof(MVMIOFileData));
542
0
    data->fd          = fd;
543
0
    data->encoding    = MVM_encoding_type_utf8;
544
0
    result->body.ops  = &op_table;
545
0
    result->body.data = data;
546
0
    return (MVMObject *)result;
547
0
}