Coverage Report

Created: 2017-04-15 07:07

/home/travis/build/MoarVM/MoarVM/src/io/fileops.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
3
#ifndef _WIN32
4
#include <sys/types.h>
5
#include <unistd.h>
6
2
#define DEFAULT_MODE 0x01B6
7
#else
8
#include <fcntl.h>
9
#define O_CREAT  _O_CREAT
10
#define O_RDONLY _O_RDONLY
11
#define O_WRONLY _O_WRONLY
12
#define O_TRUNC  _O_TRUNC
13
#define DEFAULT_MODE _S_IWRITE /* work around sucky libuv defaults */
14
#endif
15
16
166
static uv_stat_t file_info(MVMThreadContext *tc, MVMString *filename, MVMint32 use_lstat) {
17
166
    char * const a = MVM_string_utf8_c8_encode_C_string(tc, filename);
18
166
    uv_fs_t req;
19
166
20
166
    if ((use_lstat
21
13
      ? uv_fs_lstat(tc->loop, &req, a, NULL)
22
153
      :  uv_fs_stat(tc->loop, &req, a, NULL)
23
0
    ) < 0) {
24
0
        MVM_free(a);
25
0
        MVM_exception_throw_adhoc(tc, "Failed to stat file: %s", uv_strerror(req.result));
26
0
    }
27
166
28
166
    MVM_free(a);
29
166
    return req.statbuf;
30
166
}
31
32
3.69k
MVMint64 MVM_file_stat(MVMThreadContext *tc, MVMString *filename, MVMint64 status, MVMint32 use_lstat) {
33
3.69k
    MVMint64 r = -1;
34
3.69k
35
3.69k
    switch (status) {
36
3.69k
37
3.54k
        case MVM_STAT_EXISTS:             r = MVM_file_exists(tc, filename, use_lstat); break;
38
3.54k
39
1
        case MVM_STAT_FILESIZE: {
40
1
                char * const a = MVM_string_utf8_c8_encode_C_string(tc, filename);
41
1
                uv_fs_t req;
42
1
43
1
                if ((use_lstat
44
0
                  ? uv_fs_lstat(tc->loop, &req, a, NULL)
45
1
                  :  uv_fs_stat(tc->loop, &req, a, NULL)
46
0
                ) < 0) {
47
0
                    MVM_free(a);
48
0
                    MVM_exception_throw_adhoc(tc, "Failed to stat file: %s", uv_strerror(req.result));
49
0
                }
50
1
                MVM_free(a);
51
1
52
1
                r = req.statbuf.st_size;
53
1
                break;
54
3.54k
            }
55
3.54k
56
132
        case MVM_STAT_ISDIR:              r = (file_info(tc, filename, use_lstat).st_mode & S_IFMT) == S_IFDIR; break;
57
3.54k
58
2
        case MVM_STAT_ISREG:              r = (file_info(tc, filename, use_lstat).st_mode & S_IFMT) == S_IFREG; break;
59
3.54k
60
0
        case MVM_STAT_ISDEV: {
61
0
            const int mode = file_info(tc, filename, use_lstat).st_mode;
62
0
#ifdef _WIN32
63
            r = mode & S_IFMT == S_IFCHR;
64
#else
65
0
            r = (mode & S_IFMT) == S_IFCHR || (mode & S_IFMT) == S_IFBLK;
66
0
#endif
67
0
            break;
68
3.54k
        }
69
3.54k
70
0
        case MVM_STAT_CREATETIME:         r = file_info(tc, filename, use_lstat).st_birthtim.tv_sec; break;
71
3.54k
72
3
        case MVM_STAT_ACCESSTIME:         r = file_info(tc, filename, use_lstat).st_atim.tv_sec; break;
73
3.54k
74
3
        case MVM_STAT_MODIFYTIME:         r = file_info(tc, filename, use_lstat).st_mtim.tv_sec; break;
75
3.54k
76
3
        case MVM_STAT_CHANGETIME:         r = file_info(tc, filename, use_lstat).st_ctim.tv_sec; break;
77
3.54k
78
3.54k
/*        case MVM_STAT_BACKUPTIME:         r = -1; break;  */
79
3.54k
80
0
        case MVM_STAT_UID:                r = file_info(tc, filename, use_lstat).st_uid; break;
81
3.54k
82
0
        case MVM_STAT_GID:                r = file_info(tc, filename, use_lstat).st_gid; break;
83
3.54k
84
4
        case MVM_STAT_ISLNK:              r = (file_info(tc, filename, 1).st_mode & S_IFMT) == S_IFLNK; break;
85
3.54k
86
2
        case MVM_STAT_PLATFORM_DEV:       r = file_info(tc, filename, use_lstat).st_dev; break;
87
3.54k
88
2
        case MVM_STAT_PLATFORM_INODE:     r = file_info(tc, filename, use_lstat).st_ino; break;
89
3.54k
90
0
        case MVM_STAT_PLATFORM_MODE:      r = file_info(tc, filename, use_lstat).st_mode; break;
91
3.54k
92
0
        case MVM_STAT_PLATFORM_NLINKS:    r = file_info(tc, filename, use_lstat).st_nlink; break;
93
3.54k
94
0
        case MVM_STAT_PLATFORM_DEVTYPE:   r = file_info(tc, filename, use_lstat).st_rdev; break;
95
3.54k
96
0
        case MVM_STAT_PLATFORM_BLOCKSIZE: r = file_info(tc, filename, use_lstat).st_blksize; break;
97
3.54k
98
0
        case MVM_STAT_PLATFORM_BLOCKS:    r = file_info(tc, filename, use_lstat).st_blocks; break;
99
3.54k
100
0
        default: break;
101
3.69k
    }
102
3.69k
103
3.69k
    return r;
104
3.69k
}
105
106
15
MVMnum64 MVM_file_time(MVMThreadContext *tc, MVMString *filename, MVMint64 status, MVMint32 use_lstat) {
107
15
    uv_stat_t statbuf = file_info(tc, filename, use_lstat);
108
15
    uv_timespec_t ts;
109
15
110
15
    switch(status) {
111
0
        case MVM_STAT_CREATETIME: ts = statbuf.st_birthtim; break;
112
5
        case MVM_STAT_MODIFYTIME: ts = statbuf.st_mtim; break;
113
5
        case MVM_STAT_ACCESSTIME: ts = statbuf.st_atim; break;
114
5
        case MVM_STAT_CHANGETIME: ts = statbuf.st_ctim; break;
115
0
        default: return -1;
116
15
    }
117
15
118
15
    return ts.tv_sec + 1e-9 * (MVMnum64)ts.tv_nsec;
119
15
}
120
121
/* copy a file from one to another */
122
2
void MVM_file_copy(MVMThreadContext *tc, MVMString *src, MVMString * dest) {
123
2
    /* TODO: on Windows we can use the CopyFile API, which is probaly
124
2
       more efficient, not to mention easier to use. */
125
2
    uv_fs_t req;
126
2
    char * a, * b;
127
2
    uv_file in_fd = -1, out_fd = -1;
128
2
    MVMuint64 size, offset;
129
2
130
2
    a = MVM_string_utf8_c8_encode_C_string(tc, src);
131
2
    b = MVM_string_utf8_c8_encode_C_string(tc, dest);
132
2
133
2
    /* If the file cannot be stat(), there is little point in going any further. */
134
2
    if (uv_fs_stat(tc->loop, &req, a, NULL) < 0)
135
0
        goto failure;
136
2
    size = req.statbuf.st_size;
137
2
138
2
    in_fd = uv_fs_open(tc->loop, &req, (const char *)a, O_RDONLY, 0, NULL);
139
2
    if (in_fd < 0) {
140
0
        goto failure;
141
0
    }
142
2
143
2
    out_fd = uv_fs_open(tc->loop, &req, (const char *)b, O_WRONLY | O_CREAT | O_TRUNC, DEFAULT_MODE, NULL);
144
2
    if (out_fd < 0) {
145
0
        goto failure;
146
0
    }
147
2
148
2
    offset = 0;
149
2
    do {
150
2
        /* sendfile() traditionally takes offset as a pointer argument
151
2
         * used a both input and output. libuv deviates by making
152
2
         * offset an integer and returning the number of bytes
153
2
         * sent. So it is necessary to add these explicitly. */
154
2
        MVMint64 sent = uv_fs_sendfile(tc->loop, &req, out_fd, in_fd, offset, size - offset, NULL);
155
2
        if (sent < 0) {
156
0
            goto failure;
157
0
        }
158
2
        offset += sent;
159
2
    } while (offset < size);
160
2
161
2
    /* Cleanup */
162
2
    if(uv_fs_close(tc->loop, &req, in_fd, NULL) < 0) {
163
0
        goto failure;
164
0
    }
165
2
    in_fd = -1;
166
2
167
2
    if (uv_fs_close(tc->loop, &req, out_fd, NULL) < 0) {
168
0
        goto failure;
169
0
    }
170
2
171
2
    MVM_free(b);
172
2
    MVM_free(a);
173
2
    return;
174
2
175
0
 failure: {
176
0
        /* First get the error, since it may be overwritten further on. */
177
0
        const char * error = uv_strerror(req.result);
178
0
        /* Basic premise: dealing with all failure cases is hard.
179
0
         * So to simplify, a and b are allocated in all conditions.
180
0
         * Also to simplify, in_fd are nonnegative if open, negative
181
0
         * otherwise. */
182
0
        MVM_free(b);
183
0
        MVM_free(a);
184
0
        /* If any of these fail there is nothing
185
0
         * further to do, since we're already failing */
186
0
        if (in_fd >= 0)
187
0
            uv_fs_close(tc->loop, &req, in_fd, NULL);
188
0
        if (out_fd >= 0)
189
0
            uv_fs_close(tc->loop, &req, out_fd, NULL);
190
0
        /* This function only throws adhoc errors, so the message is for
191
0
         * progammer eyes only */
192
0
        MVM_exception_throw_adhoc(tc, "Failed to copy file: %s", error);
193
0
    }
194
0
}
195
196
197
/* rename one file to another. */
198
2
void MVM_file_rename(MVMThreadContext *tc, MVMString *src, MVMString *dest) {
199
2
    char * const a = MVM_string_utf8_c8_encode_C_string(tc, src);
200
2
    char * const b = MVM_string_utf8_c8_encode_C_string(tc, dest);
201
2
    uv_fs_t req;
202
2
203
2
    if(uv_fs_rename(tc->loop, &req, a, b, NULL) < 0 ) {
204
0
        MVM_free(a);
205
0
        MVM_free(b);
206
0
        MVM_exception_throw_adhoc(tc, "Failed to rename file: %s", uv_strerror(req.result));
207
0
    }
208
2
209
2
    MVM_free(a);
210
2
    MVM_free(b);
211
2
}
212
213
18
void MVM_file_delete(MVMThreadContext *tc, MVMString *f) {
214
18
    uv_fs_t req;
215
18
    char * const a = MVM_string_utf8_c8_encode_C_string(tc, f);
216
18
217
18
#ifdef _WIN32
218
    const int r = MVM_platform_unlink(a);
219
220
    if( r < 0 && errno != ENOENT) {
221
        MVM_free(a);
222
        MVM_exception_throw_adhoc(tc, "Failed to delete file: %d", errno);
223
    }
224
225
#else
226
18
    const int r = uv_fs_unlink(tc->loop, &req, a, NULL);
227
18
228
18
    if( r < 0 && r != UV_ENOENT) {
229
0
        MVM_free(a);
230
0
        MVM_exception_throw_adhoc(tc, "Failed to delete file: %s", uv_strerror(req.result));
231
0
    }
232
18
233
18
#endif
234
18
    MVM_free(a);
235
18
}
236
237
0
void MVM_file_chmod(MVMThreadContext *tc, MVMString *f, MVMint64 flag) {
238
0
    char * const a = MVM_string_utf8_c8_encode_C_string(tc, f);
239
0
    uv_fs_t req;
240
0
241
0
    if(uv_fs_chmod(tc->loop, &req, a, flag, NULL) < 0 ) {
242
0
        MVM_free(a);
243
0
        MVM_exception_throw_adhoc(tc, "Failed to set permissions on path: %s", uv_strerror(req.result));
244
0
    }
245
0
246
0
    MVM_free(a);
247
0
}
248
249
3.54k
MVMint64 MVM_file_exists(MVMThreadContext *tc, MVMString *f, MVMint32 use_lstat) {
250
3.54k
    uv_fs_t req;
251
3.54k
    char * const a = MVM_string_utf8_c8_encode_C_string(tc, f);
252
3.54k
    const MVMint64 result = (use_lstat
253
4
      ? uv_fs_lstat(tc->loop, &req, a, NULL)
254
3.54k
      :  uv_fs_stat(tc->loop, &req, a, NULL)
255
3.53k
    ) < 0 ? 0 : 1;
256
3.54k
257
3.54k
    MVM_free(a);
258
3.54k
259
3.54k
    return result;
260
3.54k
}
261
262
#ifdef _WIN32
263
#define FILE_IS(name, rwx) \
264
    MVMint64 MVM_file_is ## name (MVMThreadContext *tc, MVMString *filename, MVMint32 use_lstat) { \
265
        if (!MVM_file_exists(tc, filename, use_lstat)) \
266
            return 0; \
267
        else { \
268
            uv_stat_t statbuf = file_info(tc, filename, use_lstat); \
269
            MVMint64 r = (statbuf.st_mode & S_I ## rwx ); \
270
            return r ? 1 : 0; \
271
        } \
272
    }
273
FILE_IS(readable, READ)
274
FILE_IS(writable, WRITE)
275
MVMint64 MVM_file_isexecutable(MVMThreadContext *tc, MVMString *filename, MVMint32 use_lstat) {
276
    if (!MVM_file_exists(tc, filename, use_lstat))
277
        return 0;
278
    else {
279
        MVMint64 r = 0;
280
        uv_stat_t statbuf = file_info(tc, filename, use_lstat);
281
        if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
282
            return 1;
283
        else {
284
            /* true if fileext is in PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC */
285
            MVMString *dot = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, ".");
286
            MVMROOT(tc, dot, {
287
                MVMint64 n = MVM_string_index_from_end(tc, filename, dot, 0);
288
                if (n >= 0) {
289
                    MVMString *fileext = MVM_string_substring(tc, filename, n, -1);
290
                    char *ext  = MVM_string_utf8_c8_encode_C_string(tc, fileext);
291
                    char *pext = getenv("PATHEXT");
292
                    int plen   = strlen(pext);
293
                    int i;
294
                    for (i = 0; i < plen; i++) {
295
                        if (0 == stricmp(ext, pext++)) {
296
                             r = 1;
297
                             break;
298
                        }
299
                    }
300
                    MVM_free(ext);
301
                    MVM_free(pext);
302
                }
303
            });
304
        }
305
        return r;
306
    }
307
}
308
#else
309
#define FILE_IS(name, rwx) \
310
0
    MVMint64 MVM_file_is ## name (MVMThreadContext *tc, MVMString *filename, MVMint32 use_lstat) { \
311
0
        if (!MVM_file_exists(tc, filename, use_lstat)) \
312
0
            return 0; \
313
0
        else { \
314
0
            uv_stat_t statbuf = file_info(tc, filename, use_lstat); \
315
0
            MVMint64 r = (statbuf.st_mode & S_I ## rwx ## OTH) \
316
0
                      || (statbuf.st_uid == geteuid() && (statbuf.st_mode & S_I ## rwx ## USR)) \
317
0
                      || (statbuf.st_uid == getegid() && (statbuf.st_mode & S_I ## rwx ## GRP)); \
318
0
            return r ? 1 : 0; \
319
0
        } \
320
0
    }
Unexecuted instantiation: MVM_file_isreadable
Unexecuted instantiation: MVM_file_iswritable
Unexecuted instantiation: MVM_file_isexecutable
321
FILE_IS(readable, R)
322
FILE_IS(writable, W)
323
FILE_IS(executable, X)
324
#endif
325
326
/* Read all of a file into a string. */
327
0
MVMString * MVM_file_slurp(MVMThreadContext *tc, MVMString *filename, MVMString *encoding) {
328
0
    MVMString *mode = MVM_string_utf8_decode(tc, tc->instance->VMString, "r", 1);
329
0
    MVMObject *oshandle = (MVMObject *)MVM_file_open_fh(tc, filename, mode);
330
0
    MVMString *result;
331
0
    MVM_io_set_encoding(tc, oshandle, encoding);
332
0
    result = MVM_io_slurp(tc, oshandle);
333
0
    MVM_io_close(tc, oshandle);
334
0
    return result;
335
0
}
336
337
/* Writes a string to a file, overwriting it if necessary */
338
0
void MVM_file_spew(MVMThreadContext *tc, MVMString *output, MVMString *filename, MVMString *encoding) {
339
0
    MVMString *mode = MVM_string_utf8_decode(tc, tc->instance->VMString, "w", 1);
340
0
    MVMObject *fh = MVM_file_open_fh(tc, filename, mode);
341
0
    MVM_io_set_encoding(tc, fh, encoding);
342
0
    MVM_io_write_string(tc, fh, output, 0);
343
0
    MVM_io_close(tc, fh);
344
0
}
345
346
/* return an OSHandle representing one of the standard streams */
347
390
MVMObject * MVM_file_get_stdstream(MVMThreadContext *tc, MVMuint8 type, MVMuint8 readable) {
348
390
    switch(uv_guess_handle(type)) {
349
130
        case UV_TTY: {
350
130
            uv_tty_t * const handle = MVM_malloc(sizeof(uv_tty_t));
351
130
            uv_tty_init(tc->loop, handle, type, readable);
352
130
#ifdef _WIN32
353
            uv_stream_set_blocking((uv_stream_t *)handle, 1);
354
#else
355
130
            ((uv_stream_t *)handle)->flags = 0x80; /* UV_STREAM_BLOCKING */
356
130
#endif
357
130
            return MVM_io_syncstream_from_uvstream(tc, (uv_stream_t *)handle, 1);
358
130
        }
359
0
        case UV_FILE:
360
0
            return MVM_file_handle_from_fd(tc, type);
361
260
        case UV_NAMED_PIPE: {
362
260
            uv_pipe_t * const handle = MVM_malloc(sizeof(uv_pipe_t));
363
260
            uv_pipe_init(tc->loop, handle, 0);
364
260
#ifdef _WIN32
365
            uv_stream_set_blocking((uv_stream_t *)handle, 1);
366
#else
367
260
            ((uv_stream_t *)handle)->flags = 0x80; /* UV_STREAM_BLOCKING */
368
260
#endif
369
260
            uv_pipe_open(handle, type);
370
260
            return MVM_io_syncstream_from_uvstream(tc, (uv_stream_t *)handle, 0);
371
130
        }
372
0
        default:
373
0
            MVM_exception_throw_adhoc(tc, "get_stream failed, unsupported std handle");
374
390
    }
375
390
}
376
377
/* Takes a filename and prepends any --libpath value we have, if it's not an
378
 * absolute path. */
379
4.29k
MVMString * MVM_file_in_libpath(MVMThreadContext *tc, MVMString *orig) {
380
4.29k
    const char **lib_path = tc->instance->lib_path;
381
4.29k
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&orig);
382
4.29k
    if (lib_path) {
383
4.29k
        /* We actually have a lib_path to consider. See if the filename is
384
4.29k
         * absolute (XXX wants a platform abstraction, and doing better). */
385
4.29k
        char *orig_cstr = MVM_string_utf8_c8_encode_C_string(tc, orig);
386
4.29k
        int  absolute   = orig_cstr[0] == '/' || orig_cstr[0] == '\\' ||
387
4.29k
                          (orig_cstr[1] == ':' && orig_cstr[2] == '\\');
388
4.29k
        if (absolute) {
389
0
            /* Nothing more to do; we have an absolute path. */
390
0
            MVM_free(orig_cstr);
391
0
            MVM_gc_root_temp_pop(tc); /* orig */
392
0
            return orig;
393
0
        }
394
4.29k
        else {
395
4.29k
            MVMString *result = NULL;
396
4.29k
            int lib_path_i = 0;
397
4.29k
            MVM_gc_root_temp_push(tc, (MVMCollectable **)&result);
398
4.29k
            while (lib_path[lib_path_i]) {
399
0
                /* Concatenate libpath with filename. */
400
0
                size_t lib_path_len = strlen(lib_path[lib_path_i]);
401
0
                size_t orig_len     = strlen(orig_cstr);
402
0
                int    need_sep     = lib_path[lib_path_i][lib_path_len - 1] != '/' &&
403
0
                                      lib_path[lib_path_i][lib_path_len - 1] != '\\';
404
0
                int    new_len      = lib_path_len + (need_sep ? 1 : 0) + orig_len;
405
0
                char * new_path     = MVM_malloc(new_len);
406
0
                memcpy(new_path, lib_path[lib_path_i], lib_path_len);
407
0
                if (need_sep) {
408
0
                    new_path[lib_path_len] = '/';
409
0
                    memcpy(new_path + lib_path_len + 1, orig_cstr, orig_len);
410
0
                }
411
0
                else {
412
0
                    memcpy(new_path + lib_path_len, orig_cstr, orig_len);
413
0
                }
414
0
                result = MVM_string_utf8_c8_decode(tc, tc->instance->VMString, new_path, new_len);
415
0
                MVM_free(new_path);
416
0
                if (!MVM_file_exists(tc, result, 1))
417
0
                    result = orig;
418
0
                else {
419
0
                    MVM_free(orig_cstr);
420
0
                    MVM_gc_root_temp_pop_n(tc, 2); /* orig and result */
421
0
                    return result;
422
0
                }
423
0
                lib_path_i++;
424
0
            }
425
4.29k
            if (!result || !MVM_file_exists(tc, result, 1))
426
4.29k
                result = orig;
427
4.29k
            MVM_free(orig_cstr);
428
4.29k
            MVM_gc_root_temp_pop_n(tc, 2); /* orig and result */
429
4.29k
            return result;
430
4.29k
        }
431
4.29k
    }
432
0
    else {
433
0
        /* No libpath, so just hand back the original name. */
434
0
        MVM_gc_root_temp_pop(tc); /* orig */
435
0
        return orig;
436
0
    }
437
4.29k
}
438
439
1
void MVM_file_link(MVMThreadContext *tc, MVMString *oldpath, MVMString *newpath) {
440
1
    uv_fs_t req;
441
1
    char * const oldpath_s = MVM_string_utf8_c8_encode_C_string(tc, oldpath);
442
1
    char * const newpath_s = MVM_string_utf8_c8_encode_C_string(tc, newpath);
443
1
444
1
    if (uv_fs_link(tc->loop, &req, oldpath_s, newpath_s, NULL)) {
445
0
        MVM_free(oldpath_s);
446
0
        MVM_free(newpath_s);
447
0
        MVM_exception_throw_adhoc(tc, "Failed to link file: %s", uv_strerror(req.result));
448
0
    }
449
1
450
1
    MVM_free(oldpath_s);
451
1
    MVM_free(newpath_s);
452
1
}
453
454
3
void MVM_file_symlink(MVMThreadContext *tc, MVMString *oldpath, MVMString *newpath) {
455
3
    uv_fs_t req;
456
3
    char * const oldpath_s = MVM_string_utf8_c8_encode_C_string(tc, oldpath);
457
3
    char * const newpath_s = MVM_string_utf8_c8_encode_C_string(tc, newpath);
458
3
459
3
    if (uv_fs_symlink(tc->loop, &req, oldpath_s, newpath_s, 0, NULL)) {
460
0
        MVM_free(oldpath_s);
461
0
        MVM_free(newpath_s);
462
0
        MVM_exception_throw_adhoc(tc, "Failed to symlink file: %s", uv_strerror(req.result));
463
0
    }
464
3
465
3
    MVM_free(oldpath_s);
466
3
    MVM_free(newpath_s);
467
3
}
468
469
1
MVMString * MVM_file_readlink(MVMThreadContext *tc, MVMString *path) {
470
1
    uv_fs_t req;
471
1
    MVMString *result;
472
1
473
1
    char * const path_s = MVM_string_utf8_c8_encode_C_string(tc, path);
474
1
    if (uv_fs_readlink(tc->loop, &req, path_s, NULL) < 0) {
475
0
        MVM_free(path_s);
476
0
        MVM_exception_throw_adhoc(tc, "Failed to readlink file: %s", uv_strerror(req.result));
477
0
    }
478
1
479
1
    MVM_free(path_s);
480
1
    result = MVM_string_utf8_c8_decode(tc, tc->instance->VMString, req.ptr, strlen(req.ptr));
481
1
    MVM_free(req.ptr);
482
1
483
1
    return result;
484
1
}