[njs] Fixed callback invocations in "fs" module.

Dmitry Volyntsev xeioex at nginx.com
Wed Jan 22 12:58:12 UTC 2020


details:   https://hg.nginx.org/njs/rev/2b75ee955589
branches:  
changeset: 1314:2b75ee955589
user:      Artem S. Povalyukhin <artem.povaluhin at gmail.com>
date:      Mon Jan 20 13:24:09 2020 +0300
description:
Fixed callback invocations in "fs" module.

This closes #200 issue on Github.

diffstat:

 src/njs_fs.c             |  799 +++++++++++++++-------------------------------
 src/test/njs_unit_test.c |   34 +-
 test/njs_expect_test.exp |   42 +-
 3 files changed, 305 insertions(+), 570 deletions(-)

diffs (truncated from 1262 to 1000 lines):

diff -r b247b1c5531f -r 2b75ee955589 src/njs_fs.c
--- a/src/njs_fs.c	Wed Jan 22 15:29:47 2020 +0300
+++ b/src/njs_fs.c	Mon Jan 20 13:24:09 2020 +0300
@@ -8,37 +8,51 @@
 #include <njs_main.h>
 
 
+#define njs_fs_magic(calltype, mode)                                         \
+    (((mode) << 2) | calltype)
+
+
+typedef enum {
+    NJS_FS_DIRECT,
+    NJS_FS_PROMISE,
+    NJS_FS_CALLBACK,
+} njs_fs_calltype_t;
+
+
+typedef enum {
+    NJS_FS_TRUNC,
+    NJS_FS_APPEND,
+} njs_fs_writemode_t;
+
+
 typedef struct {
-    njs_str_t               name;
-    int                     value;
+    njs_str_t   name;
+    int         value;
 } njs_fs_entry_t;
 
 
+typedef struct {
+    int         errn;
+    const char  *desc;
+    const char  *syscall;
+} njs_fs_ioerror_t;
+
+
 static njs_int_t njs_fs_read_file(njs_vm_t *vm, njs_value_t *args,
-    njs_uint_t nargs, njs_index_t unused);
-static njs_int_t njs_fs_read_file_sync(njs_vm_t *vm, njs_value_t *args,
-    njs_uint_t nargs, njs_index_t unused);
-static njs_int_t njs_fs_append_file(njs_vm_t *vm, njs_value_t *args,
-    njs_uint_t nargs, njs_index_t unused);
+    njs_uint_t nargs, njs_index_t calltype);
 static njs_int_t njs_fs_write_file(njs_vm_t *vm, njs_value_t *args,
-    njs_uint_t nargs, njs_index_t unused);
-static njs_int_t njs_fs_append_file_sync(njs_vm_t *vm, njs_value_t *args,
-    njs_uint_t nargs, njs_index_t unused);
-static njs_int_t njs_fs_write_file_sync(njs_vm_t *vm, njs_value_t *args,
-    njs_uint_t nargs, njs_index_t unused);
-static njs_int_t njs_fs_write_file_internal(njs_vm_t *vm, njs_value_t *args,
-    njs_uint_t nargs, int default_flags);
-static njs_int_t njs_fs_write_file_sync_internal(njs_vm_t *vm,
-    njs_value_t *args, njs_uint_t nargs, int default_flags);
+    njs_uint_t nargs, njs_index_t magic);
 static njs_int_t njs_fs_rename_sync(njs_vm_t *vm, njs_value_t *args,
     njs_uint_t nargs, njs_index_t unused);
 
-static njs_int_t njs_fs_fd_read(njs_vm_t *vm, njs_value_t *path, int fd,
-    njs_str_t *data);
+static njs_int_t njs_fs_fd_read(njs_vm_t *vm, int fd, njs_str_t *data,
+    njs_fs_ioerror_t *ioerror);
 static njs_int_t njs_fs_error(njs_vm_t *vm, const char *syscall,
     const char *description, njs_value_t *path, int errn, njs_value_t *retval);
 static int njs_fs_flags(njs_str_t *value);
 static mode_t njs_fs_mode(njs_value_t *value);
+static njs_int_t njs_fs_add_event(njs_vm_t *vm, const njs_value_t *callback,
+    const njs_value_t *err, const njs_value_t *result);
 
 
 static const njs_value_t  njs_fs_errno_string = njs_string("errno");
@@ -70,8 +84,8 @@ static njs_fs_entry_t njs_flags_table[] 
 
 
 njs_inline njs_int_t
-njs_fs_path_arg(njs_vm_t *vm, const char **dst,
-    const njs_value_t* src, const njs_str_t *prop_name)
+njs_fs_path_arg(njs_vm_t *vm, const char **dst, const njs_value_t* src,
+    const njs_str_t *prop_name)
 {
     if (njs_slow_path(!njs_is_string(src))) {
         njs_type_error(vm, "\"%V\" must be a string", prop_name);
@@ -87,21 +101,33 @@ njs_fs_path_arg(njs_vm_t *vm, const char
 }
 
 
+njs_inline void
+njs_fs_set_ioerr(njs_fs_ioerror_t *ioerror, int errn, const char *desc,
+    const char *syscall)
+{
+    ioerror->errn = errn;
+    ioerror->desc = desc;
+    ioerror->syscall = syscall;
+}
+
+
 static njs_int_t
 njs_fs_read_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t unused)
+    njs_index_t calltype)
 {
-    int                 fd, errn, flags;
+    int                 fd, flags;
     u_char              *start;
     size_t              size;
     ssize_t             length;
     njs_str_t           flag, encoding, data;
     njs_int_t           ret;
-    const char          *path, *syscall, *description;
+    const char          *path;
     struct stat         sb;
-    njs_value_t         *callback, arguments[3];
+    const njs_value_t   *callback, *options;
+    njs_value_t         err;
     njs_object_prop_t   *prop;
     njs_lvlhsh_query_t  lhq;
+    njs_fs_ioerror_t    ioerror;
 
     ret = njs_fs_path_arg(vm, &path, njs_arg(args, nargs, 1),
                           &njs_str_value("path"));
@@ -109,247 +135,28 @@ njs_fs_read_file(njs_vm_t *vm, njs_value
         return ret;
     }
 
-    if (njs_slow_path(nargs < 3)) {
-        njs_type_error(vm, "too few arguments");
-        return NJS_ERROR;
+    options = njs_arg(args, nargs, 2);
+
+    if (njs_slow_path(calltype == NJS_FS_CALLBACK)) {
+        callback = njs_arg(args, nargs, njs_min(nargs - 1, 3));
+        if (!njs_is_function(callback)) {
+            njs_type_error(vm, "\"callback\" must be a function");
+            return NJS_ERROR;
+        }
+        if (options == callback) {
+            options = &njs_value_undefined;
+        }
+
+    } else {
+        /* GCC complains about uninitialized callback. */
+        callback = NULL;
     }
 
     flag.start = NULL;
     encoding.length = 0;
     encoding.start = NULL;
 
-    if (!njs_is_function(&args[2])) {
-        if (njs_is_string(&args[2])) {
-            njs_string_get(&args[2], &encoding);
-
-        } else if (njs_is_object(&args[2])) {
-            lhq.key_hash = NJS_FLAG_HASH;
-            lhq.key = njs_str_value("flag");
-            lhq.proto = &njs_object_hash_proto;
-
-            ret = njs_lvlhsh_find(njs_object_hash(&args[2]), &lhq);
-            if (ret == NJS_OK) {
-                prop = lhq.value;
-                njs_string_get(&prop->value, &flag);
-            }
-
-            lhq.key_hash = NJS_ENCODING_HASH;
-            lhq.key = njs_str_value("encoding");
-            lhq.proto = &njs_object_hash_proto;
-
-            ret = njs_lvlhsh_find(njs_object_hash(&args[2]), &lhq);
-            if (ret == NJS_OK) {
-                prop = lhq.value;
-                njs_string_get(&prop->value, &encoding);
-            }
-
-        } else {
-            njs_type_error(vm, "Unknown options type "
-                           "(a string or object required)");
-            return NJS_ERROR;
-        }
-
-        if (njs_slow_path(nargs < 4 || !njs_is_function(&args[3]))) {
-            njs_type_error(vm, "callback must be a function");
-            return NJS_ERROR;
-        }
-
-        callback = &args[3];
-
-    } else {
-        if (njs_slow_path(!njs_is_function(&args[2]))) {
-            njs_type_error(vm, "callback must be a function");
-            return NJS_ERROR;
-        }
-
-        callback = &args[2];
-    }
-
-    if (flag.start == NULL) {
-        flag = njs_str_value("r");
-    }
-
-    flags = njs_fs_flags(&flag);
-    if (njs_slow_path(flags == -1)) {
-        njs_type_error(vm, "Unknown file open flags: \"%V\"", &flag);
-        return NJS_ERROR;
-    }
-
-    if (encoding.length != 0
-        && (encoding.length != 4 || memcmp(encoding.start, "utf8", 4) != 0))
-    {
-        njs_type_error(vm, "Unknown encoding: \"%V\"", &encoding);
-        return NJS_ERROR;
-    }
-
-    description = NULL;
-
-    /* GCC 4 complains about uninitialized errn and syscall. */
-    errn = 0;
-    syscall = NULL;
-
-    fd = open(path, flags);
-    if (njs_slow_path(fd < 0)) {
-        errn = errno;
-        description = strerror(errno);
-        syscall = "open";
-        goto done;
-    }
-
-    ret = fstat(fd, &sb);
-    if (njs_slow_path(ret == -1)) {
-        errn = errno;
-        description = strerror(errno);
-        syscall = "stat";
-        goto done;
-    }
-
-    if (njs_slow_path(!S_ISREG(sb.st_mode))) {
-        errn = 0;
-        description = "File is not regular";
-        syscall = "stat";
-        goto done;
-    }
-
-    if (encoding.length != 0) {
-        length = sb.st_size;
-
-        if (length > NJS_STRING_MAP_STRIDE) {
-            /*
-             * At this point length is not known, in order to set it to
-             * the correct value after file is read, we need to ensure that
-             * offset_map is allocated by njs_string_alloc(). This can be
-             * achieved by making length != size.
-             */
-            length += 1;
-        }
-
-    } else {
-        length = 0;
-    }
-
-    size = sb.st_size;
-
-    if (njs_fast_path(size != 0)) {
-
-        start = njs_string_alloc(vm, &arguments[2], size, length);
-        if (njs_slow_path(start == NULL)) {
-            goto fail;
-        }
-
-        data.start = start;
-        data.length = size;
-
-        ret = njs_fs_fd_read(vm, &args[1], fd, &data);
-        if (ret != NJS_OK) {
-            goto fail;
-        }
-
-        start = data.start;
-
-    } else {
-        /* size of the file is not known in advance. */
-
-        data.length = 0;
-
-        ret = njs_fs_fd_read(vm, &args[1], fd, &data);
-        if (ret != NJS_OK) {
-            goto fail;
-        }
-
-        size = data.length;
-        start = data.start;
-
-        ret = njs_string_new(vm, &arguments[2], start, size, length);
-        if (njs_slow_path(ret != NJS_OK)) {
-            goto fail;
-        }
-    }
-
-    if (encoding.length != 0) {
-        length = njs_utf8_length(start, size);
-
-        if (length >= 0) {
-            njs_string_length_set(&arguments[2], length);
-
-        } else {
-            errn = 0;
-            description = "Non-UTF8 file, convertion is not implemented";
-            syscall = NULL;
-            goto done;
-        }
-    }
-
-done:
-
-    if (fd != -1) {
-        (void) close(fd);
-    }
-
-    if (description != 0) {
-        (void) njs_fs_error(vm, syscall, description, &args[1], errn,
-                            &arguments[1]);
-
-        njs_set_undefined(&arguments[2]);
-
-    } else {
-        njs_set_undefined(&arguments[1]);
-    }
-
-    njs_set_undefined(&arguments[0]);
-
-    ret = njs_function_apply(vm, njs_function(callback), arguments, 3,
-                             &vm->retval);
-
-    if (njs_slow_path(ret != NJS_OK)) {
-        return ret;
-    }
-
-    njs_set_undefined(&vm->retval);
-
-    return NJS_OK;
-
-fail:
-
-    if (fd != -1) {
-        (void) close(fd);
-    }
-
-    return NJS_ERROR;
-}
-
-
-static njs_int_t
-njs_fs_read_file_sync(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t unused)
-{
-    int                 fd, errn, flags;
-    u_char              *start;
-    size_t              size;
-    ssize_t             length;
-    njs_str_t           flag, encoding, data;
-    njs_int_t           ret;
-    const char          *path, *syscall, *description;
-    struct stat         sb;
-    njs_object_prop_t   *prop;
-    njs_lvlhsh_query_t  lhq;
-
-    ret = njs_fs_path_arg(vm, &path, njs_arg(args, nargs, 1),
-                          &njs_str_value("path"));
-    if (njs_slow_path(ret != NJS_OK)) {
-        return ret;
-    }
-
-    if (njs_slow_path(nargs < 2)) {
-        njs_type_error(vm, "too few arguments");
-        return NJS_ERROR;
-    }
-
-    flag.start = NULL;
-    encoding.length = 0;
-    encoding.start = NULL;
-
-    if (nargs == 3) {
+    if (njs_slow_path(!njs_is_undefined(options))) {
         if (njs_is_string(&args[2])) {
             njs_string_get(&args[2], &encoding);
 
@@ -391,11 +198,6 @@ njs_fs_read_file_sync(njs_vm_t *vm, njs_
         return NJS_ERROR;
     }
 
-    path = njs_string_to_c_string(vm, &args[1]);
-    if (njs_slow_path(path == NULL)) {
-        return NJS_ERROR;
-    }
-
     if (encoding.length != 0
         && (encoding.length != 4 || memcmp(encoding.start, "utf8", 4) != 0))
     {
@@ -403,32 +205,22 @@ njs_fs_read_file_sync(njs_vm_t *vm, njs_
         return NJS_ERROR;
     }
 
-    description = NULL;
-
-    /* GCC 4 complains about uninitialized errn and syscall. */
-    errn = 0;
-    syscall = NULL;
+    njs_fs_set_ioerr(&ioerror, 0, NULL, NULL);
 
     fd = open(path, flags);
     if (njs_slow_path(fd < 0)) {
-        errn = errno;
-        description = strerror(errno);
-        syscall = "open";
+        njs_fs_set_ioerr(&ioerror, errno, strerror(errno), "open");
         goto done;
     }
 
     ret = fstat(fd, &sb);
     if (njs_slow_path(ret == -1)) {
-        errn = errno;
-        description = strerror(errno);
-        syscall = "stat";
+        njs_fs_set_ioerr(&ioerror, errno, strerror(errno), "fstat");
         goto done;
     }
 
     if (njs_slow_path(!S_ISREG(sb.st_mode))) {
-        errn = 0;
-        description = "File is not regular";
-        syscall = "stat";
+        njs_fs_set_ioerr(&ioerror, 0, "File is not regular", "stat");
         goto done;
     }
 
@@ -452,7 +244,6 @@ njs_fs_read_file_sync(njs_vm_t *vm, njs_
     size = sb.st_size;
 
     if (njs_fast_path(size != 0)) {
-
         start = njs_string_alloc(vm, &vm->retval, size, length);
         if (njs_slow_path(start == NULL)) {
             goto fail;
@@ -461,8 +252,12 @@ njs_fs_read_file_sync(njs_vm_t *vm, njs_
         data.start = start;
         data.length = size;
 
-        ret = njs_fs_fd_read(vm, &args[1], fd, &data);
+        ret = njs_fs_fd_read(vm, fd, &data, &ioerror);
         if (ret != NJS_OK) {
+            if (ioerror.desc != NULL) {
+                goto done;
+            }
+
             goto fail;
         }
 
@@ -473,8 +268,12 @@ njs_fs_read_file_sync(njs_vm_t *vm, njs_
 
         data.length = 0;
 
-        ret = njs_fs_fd_read(vm, &args[1], fd, &data);
+        ret = njs_fs_fd_read(vm, fd, &data, &ioerror);
         if (ret != NJS_OK) {
+            if (ioerror.desc != NULL) {
+                goto done;
+            }
+
             goto fail;
         }
 
@@ -494,9 +293,9 @@ njs_fs_read_file_sync(njs_vm_t *vm, njs_
             njs_string_length_set(&vm->retval, length);
 
         } else {
-            errn = 0;
-            description = "Non-UTF8 file, convertion is not implemented";
-            syscall = NULL;
+            njs_fs_set_ioerr(&ioerror, 0,
+                             "Non-UTF8 file, convertion is not implemented",
+                             NULL);
             goto done;
         }
     }
@@ -507,12 +306,46 @@ done:
         (void) close(fd);
     }
 
-    if (description != 0) {
-        return njs_fs_error(vm, syscall, description, &args[1], errn,
-                            &vm->retval);
+    if (njs_fast_path(calltype == NJS_FS_DIRECT)) {
+        if (njs_slow_path(ioerror.desc != NULL)) {
+            (void) njs_fs_error(vm, ioerror.syscall, ioerror.desc,
+                                &args[1], ioerror.errn, &vm->retval);
+            return NJS_ERROR;
+        }
+
+        return NJS_OK;
+    }
+
+    if (calltype == NJS_FS_PROMISE) {
+        njs_internal_error(vm, "promise callback is not implemented");
+        return NJS_ERROR;
     }
 
-    return NJS_OK;
+    if (calltype == NJS_FS_CALLBACK) {
+        if (njs_slow_path(ioerror.desc)) {
+            ret = njs_fs_error(vm, ioerror.syscall, ioerror.desc,
+                               &args[1], ioerror.errn, &err);
+            if (njs_slow_path(ret != NJS_OK)) {
+                return ret;
+            }
+
+            njs_set_undefined(&vm->retval);
+
+        } else {
+            njs_set_undefined(&err);
+        }
+
+        ret = njs_fs_add_event(vm, callback, &err, &vm->retval);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return ret;
+        }
+
+        njs_set_undefined(&vm->retval);
+        return NJS_OK;
+    }
+
+    njs_internal_error(vm, "invalid calltype");
+    return NJS_ERROR;
 
 fail:
 
@@ -525,50 +358,20 @@ fail:
 
 
 static njs_int_t
-njs_fs_append_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t unused)
-{
-    return njs_fs_write_file_internal(vm, args, nargs,
-                                      O_APPEND | O_CREAT | O_WRONLY);
-}
-
-
-static njs_int_t
 njs_fs_write_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t unused)
+    njs_index_t magic)
 {
-    return njs_fs_write_file_internal(vm, args, nargs,
-                                      O_TRUNC | O_CREAT | O_WRONLY);
-}
-
-
-static njs_int_t njs_fs_append_file_sync(njs_vm_t *vm, njs_value_t *args,
-    njs_uint_t nargs, njs_index_t unused)
-{
-    return njs_fs_write_file_sync_internal(vm, args, nargs,
-                                           O_APPEND | O_CREAT | O_WRONLY);
-}
-
-
-static njs_int_t njs_fs_write_file_sync(njs_vm_t *vm, njs_value_t *args,
-    njs_uint_t nargs, njs_index_t unused)
-{
-    return njs_fs_write_file_sync_internal(vm, args, nargs,
-                                           O_TRUNC | O_CREAT | O_WRONLY);
-}
-
-
-static njs_int_t njs_fs_write_file_internal(njs_vm_t *vm, njs_value_t *args,
-    njs_uint_t nargs, int default_flags)
-{
-    int                 fd, errn, flags;
+    int                 fd, flags;
     u_char              *p, *end;
     mode_t              md;
     ssize_t             n;
     njs_str_t           data, flag, encoding;
     njs_int_t           ret;
-    const char          *path, *syscall, *description;
-    njs_value_t         *callback, *mode, arguments[2];
+    const char          *path;
+    njs_value_t         *mode, err;
+    njs_fs_calltype_t   calltype;
+    const njs_value_t   *callback, *options;
+    njs_fs_ioerror_t    ioerror;
     njs_object_prop_t   *prop;
     njs_lvlhsh_query_t  lhq;
 
@@ -578,14 +381,24 @@ static njs_int_t njs_fs_write_file_inter
         return ret;
     }
 
-    if (njs_slow_path(nargs < 4)) {
-        njs_type_error(vm, "too few arguments");
+    if (njs_slow_path(!njs_is_string(njs_arg(args, nargs, 2)))) {
+        njs_type_error(vm, "\"data\" must be a string");
         return NJS_ERROR;
     }
 
-    if (njs_slow_path(!njs_is_string(&args[2]))) {
-        njs_type_error(vm, "data must be a string");
-        return NJS_ERROR;
+    callback = NULL;
+    calltype = magic & 3;
+    options = njs_arg(args, nargs, 3);
+
+    if (njs_slow_path(calltype == NJS_FS_CALLBACK)) {
+        callback = njs_arg(args, nargs, njs_min(nargs - 1, 4));
+        if (!njs_is_function(callback)) {
+            njs_type_error(vm, "\"callback\" must be a function");
+            return NJS_ERROR;
+        }
+        if (options == callback) {
+            options = &njs_value_undefined;
+        }
     }
 
     mode = NULL;
@@ -595,191 +408,7 @@ static njs_int_t njs_fs_write_file_inter
     encoding.length = 0;
     encoding.start = NULL;
 
-    if (!njs_is_function(&args[3])) {
-        if (njs_is_string(&args[3])) {
-            njs_string_get(&args[3], &encoding);
-
-        } else if (njs_is_object(&args[3])) {
-            lhq.key_hash = NJS_FLAG_HASH;
-            lhq.key = njs_str_value("flag");
-            lhq.proto = &njs_object_hash_proto;
-
-            ret = njs_lvlhsh_find(njs_object_hash(&args[3]), &lhq);
-            if (ret == NJS_OK) {
-                prop = lhq.value;
-                njs_string_get(&prop->value, &flag);
-            }
-
-            lhq.key_hash = NJS_ENCODING_HASH;
-            lhq.key = njs_str_value("encoding");
-            lhq.proto = &njs_object_hash_proto;
-
-            ret = njs_lvlhsh_find(njs_object_hash(&args[3]), &lhq);
-            if (ret == NJS_OK) {
-                prop = lhq.value;
-                njs_string_get(&prop->value, &encoding);
-            }
-
-            lhq.key_hash = NJS_MODE_HASH;
-            lhq.key = njs_str_value("mode");
-            lhq.proto = &njs_object_hash_proto;
-
-            ret = njs_lvlhsh_find(njs_object_hash(&args[3]), &lhq);
-            if (ret == NJS_OK) {
-                prop = lhq.value;
-                mode = &prop->value;
-            }
-
-        } else {
-            njs_type_error(vm, "Unknown options type "
-                           "(a string or object required)");
-            return NJS_ERROR;
-        }
-
-        if (njs_slow_path(nargs < 5 || !njs_is_function(&args[4]))) {
-            njs_type_error(vm, "callback must be a function");
-            return NJS_ERROR;
-        }
-
-        callback = &args[4];
-
-    } else {
-        if (njs_slow_path(!njs_is_function(&args[3]))) {
-            njs_type_error(vm, "callback must be a function");
-            return NJS_ERROR;
-        }
-
-        callback = &args[3];
-    }
-
-    if (flag.start != NULL) {
-        flags = njs_fs_flags(&flag);
-        if (njs_slow_path(flags == -1)) {
-            njs_type_error(vm, "Unknown file open flags: \"%V\"", &flag);
-            return NJS_ERROR;
-        }
-
-    } else {
-        flags = default_flags;
-    }
-
-    if (mode != NULL) {
-        md = njs_fs_mode(mode);
-
-    } else {
-        md = 0666;
-    }
-
-    if (encoding.length != 0
-        && (encoding.length != 4 || memcmp(encoding.start, "utf8", 4) != 0))
-    {
-        njs_type_error(vm, "Unknown encoding: \"%V\"", &encoding);
-        return NJS_ERROR;
-    }
-
-    description = NULL;
-
-    /* GCC 4 complains about uninitialized errn and syscall. */
-    errn = 0;
-    syscall = NULL;
-
-    fd = open(path, flags, md);
-    if (njs_slow_path(fd < 0)) {
-        errn = errno;
-        description = strerror(errno);
-        syscall = "open";
-        goto done;
-    }
-
-    njs_string_get(&args[2], &data);
-
-    p = data.start;
-    end = p + data.length;
-
-    while (p < end) {
-        n = write(fd, p, end - p);
-        if (njs_slow_path(n == -1)) {
-            if (errno == EINTR) {
-                continue;
-            }
-
-            errn = errno;
-            description = strerror(errno);
-            syscall = "write";
-            goto done;
-        }
-
-        p += n;
-    }
-
-done:
-
-    if (fd != -1) {
-        (void) close(fd);
-    }
-
-    if (description != 0) {
-        (void) njs_fs_error(vm, syscall, description, &args[1], errn,
-                            &arguments[1]);
-
-    } else {
-        njs_set_undefined(&arguments[1]);
-    }
-
-    njs_set_undefined(&arguments[0]);
-
-    ret = njs_function_apply(vm, njs_function(callback), arguments, 2,
-                             &vm->retval);
-
-    if (njs_slow_path(ret != NJS_OK)) {
-        return ret;
-    }
-
-    njs_set_undefined(&vm->retval);
-
-    return NJS_OK;
-}
-
-
-static njs_int_t
-njs_fs_write_file_sync_internal(njs_vm_t *vm, njs_value_t *args,
-    njs_uint_t nargs, int default_flags)
-{
-    int                 fd, errn, flags;
-    u_char              *p, *end;
-    mode_t              md;
-    ssize_t             n;
-    njs_str_t           data, flag, encoding;
-    njs_int_t           ret;
-    const char          *path, *syscall, *description;
-    njs_value_t         *mode;
-    njs_object_prop_t   *prop;
-    njs_lvlhsh_query_t  lhq;
-
-    ret = njs_fs_path_arg(vm, &path, njs_arg(args, nargs, 1),
-                          &njs_str_value("path"));
-    if (njs_slow_path(ret != NJS_OK)) {
-        return ret;
-    }
-
-    if (njs_slow_path(nargs < 3)) {
-        njs_type_error(vm, "too few arguments");
-        return NJS_ERROR;
-    }
-
-    if (njs_slow_path(!njs_is_string(&args[2]))) {
-        njs_type_error(vm, "data must be a string");
-        return NJS_ERROR;
-    }
-
-    mode = NULL;
-    /* GCC complains about uninitialized flag.length. */
-    flag.length = 0;
-    flag.start = NULL;
-    encoding.length = 0;
-    encoding.start = NULL;
-
-    if (nargs == 4) {
+    if (njs_slow_path(!njs_is_undefined(options))) {
         if (njs_is_string(&args[3])) {
             njs_string_get(&args[3], &encoding);
 
@@ -829,7 +458,8 @@ njs_fs_write_file_sync_internal(njs_vm_t
         }
 
     } else {
-        flags = default_flags;
+        flags = O_CREAT | O_WRONLY;
+        flags |= ((magic >> 2) == NJS_FS_APPEND) ? O_APPEND : O_TRUNC;
     }
 
     if (mode != NULL) {
@@ -846,17 +476,11 @@ njs_fs_write_file_sync_internal(njs_vm_t
         return NJS_ERROR;
     }
 
-    description = NULL;
-
-    /* GCC 4 complains about uninitialized errn and syscall. */
-    errn = 0;
-    syscall = NULL;
+    njs_fs_set_ioerr(&ioerror, 0, NULL, NULL);
 
     fd = open(path, flags, md);
     if (njs_slow_path(fd < 0)) {
-        errn = errno;
-        description = strerror(errno);
-        syscall = "open";
+        njs_fs_set_ioerr(&ioerror, errno, strerror(errno), "open");
         goto done;
     }
 
@@ -872,9 +496,7 @@ njs_fs_write_file_sync_internal(njs_vm_t
                 continue;
             }
 
-            errn = errno;
-            description = strerror(errno);
-            syscall = "write";
+            njs_fs_set_ioerr(&ioerror, errno, strerror(errno), "write");
             goto done;
         }
 
@@ -887,15 +509,45 @@ done:
         (void) close(fd);
     }
 
-    if (description != 0) {
-        return njs_fs_error(vm, syscall, description, &args[1], errn,
-                            &vm->retval);
+    if (njs_fast_path(calltype == NJS_FS_DIRECT)) {
+        if (njs_slow_path(ioerror.desc != NULL)) {
+            (void) njs_fs_error(vm, ioerror.syscall, ioerror.desc, &args[1],
+                                ioerror.errn, &vm->retval);
+            return NJS_ERROR;
+        }
 
-    } else {
         njs_set_undefined(&vm->retval);
+        return NJS_OK;
+    }
+
+    if (calltype == NJS_FS_PROMISE) {
+        njs_internal_error(vm, "not implemented");
+        return NJS_ERROR;
     }
 
-    return NJS_OK;
+    if (calltype == NJS_FS_CALLBACK) {
+        if (ioerror.desc != NULL) {
+            ret = njs_fs_error(vm, ioerror.syscall, ioerror.desc, &args[1],
+                               ioerror.errn, &err);
+            if (njs_slow_path(ret != NJS_OK)) {
+                return ret;
+            }
+
+        } else {
+            njs_set_undefined(&err);
+        }
+
+        ret = njs_fs_add_event(vm, callback, &err, NULL);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return ret;
+        }
+
+        njs_set_undefined(&vm->retval);
+        return NJS_OK;
+    }
+
+    njs_internal_error(vm, "invalid calltype");
+    return NJS_ERROR;
 }
 
 
@@ -920,8 +572,9 @@ njs_fs_rename_sync(njs_vm_t *vm, njs_val
 
     ret = rename(old_path, new_path);
     if (njs_slow_path(ret != 0)) {
-        return njs_fs_error(vm, "rename", strerror(errno), NULL, errno,
+        (void) njs_fs_error(vm, "rename", strerror(errno), NULL, errno,
                             &vm->retval);
+        return NJS_ERROR;
     }
 
     njs_set_undefined(&vm->retval);
@@ -931,7 +584,8 @@ njs_fs_rename_sync(njs_vm_t *vm, njs_val
 
 
 static njs_int_t
-njs_fs_fd_read(njs_vm_t *vm, njs_value_t *path, int fd, njs_str_t *data)
+njs_fs_fd_read(njs_vm_t *vm, int fd, njs_str_t *data,
+    njs_fs_ioerror_t *ioerror)
 {
     u_char   *p, *end, *start;
     size_t   size;
@@ -956,8 +610,8 @@ njs_fs_fd_read(njs_vm_t *vm, njs_value_t
         n = read(fd, p, end - p);
 
         if (njs_slow_path(n < 0)) {
-            return njs_fs_error(vm, "read", strerror(errno), path, errno,
-                                &vm->retval);
+            njs_fs_set_ioerr(ioerror, errno, strerror(errno), "read");
+            return NJS_DECLINED;
         }
 
         p += n;
@@ -1084,7 +738,7 @@ njs_fs_error(njs_vm_t *vm, const char *s
 
     njs_set_object(retval, error);
 
-    return NJS_ERROR;
+    return NJS_OK;
 }
 
 
@@ -1127,6 +781,61 @@ njs_fs_mode(njs_value_t *value)
 }
 
 
+static njs_int_t
+njs_fs_add_event(njs_vm_t *vm, const njs_value_t *callback,
+    const njs_value_t *err, const njs_value_t *result)
+{
+    njs_int_t     nargs;
+    njs_event_t   *event;
+    njs_vm_ops_t  *ops;
+
+    nargs = (result == NULL) ? 1 : 2;
+
+    ops = vm->options.ops;
+    if (njs_slow_path(ops == NULL)) {
+        njs_internal_error(vm, "not supported by host environment");
+        return NJS_ERROR;
+    }
+
+    event = njs_mp_alloc(vm->mem_pool, sizeof(njs_event_t));
+    if (njs_slow_path(event == NULL)) {
+        goto memory_error;
+    }
+
+    event->destructor = ops->clear_timer;
+    event->function = njs_function(callback);
+    event->nargs = nargs;
+    event->once = 1;
+    event->posted = 0;
+
+    event->args = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t) * nargs);
+    if (njs_slow_path(event->args == NULL)) {
+        goto memory_error;
+    }
+
+    /* GC: retain */
+    event->args[0] = *err;
+
+    if (nargs == 2) {
+        event->args[1] = *result;
+    }
+
+    event->host_event = ops->set_timer(vm->external, 0, event);
+    if (njs_slow_path(event->host_event == NULL)) {
+        njs_internal_error(vm, "set_timer() failed");
+        return NJS_ERROR;
+    }
+
+    return njs_add_event(vm, event);
+


More information about the nginx-devel mailing list