[njs] Added fs.Stats, fs.stat() and friends.

Dmitry Volyntsev xeioex at nginx.com
Wed Nov 3 15:51:21 UTC 2021


details:   https://hg.nginx.org/njs/rev/203bc61d8d70
branches:  
changeset: 1739:203bc61d8d70
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Wed Nov 03 15:46:15 2021 +0000
description:
Added fs.Stats, fs.stat() and friends.

diffstat:

 auto/stat                |   58 +++
 configure                |    1 +
 src/njs_builtin.c        |    1 +
 src/njs_fs.c             |  722 ++++++++++++++++++++++++++++++++++++++++++++++-
 src/njs_fs.h             |    1 +
 src/njs_value.h          |    1 +
 src/njs_vm.h             |    1 +
 test/fs/methods.js       |  186 ++++++++++++
 test/njs_expect_test.exp |    8 +-
 9 files changed, 970 insertions(+), 9 deletions(-)

diffs (truncated from 1117 to 1000 lines):

diff -r 4ddfb2f2227f -r 203bc61d8d70 auto/stat
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/auto/stat	Wed Nov 03 15:46:15 2021 +0000
@@ -0,0 +1,58 @@
+
+# Copyright (C) Dmitry Volyntsev
+# Copyright (C) NGINX, Inc.
+
+
+njs_feature="stat.st_atimespec"
+njs_feature_name=NJS_HAVE_STAT_ATIMESPEC
+njs_feature_run=no
+njs_feature_incs=
+njs_feature_libs=
+njs_feature_test="#include <sys/stat.h>
+
+                 int main(void) {
+                     struct stat st;
+
+                     if (fstat(0, &st) != 0) {
+                        return 1;
+                     }
+
+                     return (int) st.st_atimespec.tv_sec;
+                 }"
+. auto/feature
+
+
+njs_feature="stat.st_birthtim"
+njs_feature_name=NJS_HAVE_STAT_BIRTHTIM
+njs_feature_incs=
+njs_feature_libs=
+njs_feature_test="#include <sys/stat.h>
+
+                  int main(void) {
+                      struct stat st;
+
+                      if (fstat(0, &st) != 0) {
+                         return 1;
+                      }
+
+                      return (int) st.st_birthtim.tv_sec;
+                  }"
+. auto/feature
+
+
+njs_feature="stat.st_atim"
+njs_feature_name=NJS_HAVE_STAT_ATIM
+njs_feature_incs=
+njs_feature_libs=
+njs_feature_test="#include <sys/stat.h>
+
+                  int main(void) {
+                      struct stat st;
+
+                      if (fstat(0, &st) != 0) {
+                         return 1;
+                      }
+
+                      return (int) st.st_atim.tv_sec;
+                  }"
+. auto/feature
diff -r 4ddfb2f2227f -r 203bc61d8d70 configure
--- a/configure	Tue Nov 02 12:38:42 2021 +0000
+++ b/configure	Wed Nov 03 15:46:15 2021 +0000
@@ -23,6 +23,7 @@ set -u
 . auto/time
 . auto/memalign
 . auto/getrandom
+. auto/stat
 . auto/explicit_bzero
 . auto/pcre
 . auto/readline
diff -r 4ddfb2f2227f -r 203bc61d8d70 src/njs_builtin.c
--- a/src/njs_builtin.c	Tue Nov 02 12:38:42 2021 +0000
+++ b/src/njs_builtin.c	Wed Nov 03 15:46:15 2021 +0000
@@ -89,6 +89,7 @@ static const njs_object_type_init_t *con
     &njs_iterator_type_init,
     &njs_array_iterator_type_init,
     &njs_dirent_type_init,
+    &njs_stats_type_init,
     &njs_hash_type_init,
     &njs_hmac_type_init,
     &njs_typed_array_type_init,
diff -r 4ddfb2f2227f -r 203bc61d8d70 src/njs_fs.c
--- a/src/njs_fs.c	Tue Nov 02 12:38:42 2021 +0000
+++ b/src/njs_fs.c	Wed Nov 03 15:46:15 2021 +0000
@@ -12,12 +12,12 @@
 #if (NJS_SOLARIS)
 
 #define DT_DIR         0
-#define DT_REG         0
-#define DT_CHR         0
-#define DT_LNK         0
-#define DT_BLK         0
-#define DT_FIFO        0
-#define DT_SOCK        0
+#define DT_REG         1
+#define DT_CHR         2
+#define DT_LNK         3
+#define DT_BLK         4
+#define DT_FIFO        5
+#define DT_SOCK        6
 #define NJS_DT_INVALID 0xffffffff
 
 #define njs_dentry_type(_dentry)                                             \
@@ -50,9 +50,15 @@ typedef enum {
 } njs_fs_writemode_t;
 
 
+typedef enum {
+    NJS_FS_STAT,
+    NJS_FS_LSTAT,
+} njs_fs_statmode_t;
+
+
 typedef struct {
-    njs_str_t   name;
-    int         value;
+    njs_str_t       name;
+    int             value;
 } njs_fs_entry_t;
 
 
@@ -74,6 +80,48 @@ typedef enum {
 } njs_ftw_type_t;
 
 
+typedef struct {
+    long tv_sec;
+    long tv_nsec;
+} njs_timespec_t;
+
+
+typedef struct {
+    uint64_t        st_dev;
+    uint64_t        st_mode;
+    uint64_t        st_nlink;
+    uint64_t        st_uid;
+    uint64_t        st_gid;
+    uint64_t        st_rdev;
+    uint64_t        st_ino;
+    uint64_t        st_size;
+    uint64_t        st_blksize;
+    uint64_t        st_blocks;
+    njs_timespec_t  st_atim;
+    njs_timespec_t  st_mtim;
+    njs_timespec_t  st_ctim;
+    njs_timespec_t  st_birthtim;
+} njs_stat_t;
+
+
+typedef enum {
+    NJS_FS_STAT_DEV,
+    NJS_FS_STAT_INO,
+    NJS_FS_STAT_MODE,
+    NJS_FS_STAT_NLINK,
+    NJS_FS_STAT_UID,
+    NJS_FS_STAT_GID,
+    NJS_FS_STAT_RDEV,
+    NJS_FS_STAT_SIZE,
+    NJS_FS_STAT_BLKSIZE,
+    NJS_FS_STAT_BLOCKS,
+    NJS_FS_STAT_ATIME,
+    NJS_FS_STAT_BIRTHTIME,
+    NJS_FS_STAT_CTIME,
+    NJS_FS_STAT_MTIME,
+} njs_stat_prop_t;
+
+
 typedef njs_int_t (*njs_file_tree_walk_cb_t)(const char *, const struct stat *,
      njs_ftw_type_t);
 
@@ -105,6 +153,9 @@ static njs_int_t njs_fs_add_event(njs_vm
 static njs_int_t njs_fs_dirent_create(njs_vm_t *vm, njs_value_t *name,
     njs_value_t *type, njs_value_t *retval);
 
+static njs_int_t njs_fs_stats_create(njs_vm_t *vm, struct stat *st,
+    njs_value_t *retval);
+
 static njs_fs_entry_t njs_flags_table[] = {
     { njs_str("a"),   O_APPEND | O_CREAT | O_WRONLY },
     { njs_str("a+"),  O_APPEND | O_CREAT | O_RDWR },
@@ -1010,6 +1061,102 @@ done:
 
 
 static njs_int_t
+njs_fs_stat(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+    njs_index_t magic)
+{
+    njs_int_t          ret;
+    njs_bool_t         throw;
+    struct stat        sb;
+    const char         *path;
+    njs_value_t        retval, *callback, *options;
+    njs_fs_calltype_t  calltype;
+    char               path_buf[NJS_MAX_PATH + 1];
+
+    static const njs_value_t  string_bigint = njs_string("bigint");
+    static const njs_value_t  string_throw = njs_string("throwIfNoEntry");
+
+    path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path");
+    if (njs_slow_path(path == NULL)) {
+        return NJS_ERROR;
+    }
+
+    callback = NULL;
+    calltype = magic & 3;
+    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_arg(&njs_value_undefined);
+        }
+    }
+
+    throw = 1;
+
+    switch (options->type) {
+    case NJS_UNDEFINED:
+        break;
+
+    default:
+        if (!njs_is_object(options)) {
+            njs_type_error(vm, "Unknown options type: \"%s\" "
+                           "(an object required)",
+                           njs_type_string(options->type));
+            return NJS_ERROR;
+        }
+
+        ret = njs_value_property(vm, options, njs_value_arg(&string_bigint),
+                                 &retval);
+        if (njs_slow_path(ret == NJS_ERROR)) {
+            return ret;
+        }
+
+        if (njs_bool(&retval)) {
+            njs_type_error(vm, "\"bigint\" is not supported");
+            return NJS_ERROR;
+        }
+
+        if (calltype == NJS_FS_DIRECT) {
+            ret = njs_value_property(vm, options, njs_value_arg(&string_throw),
+                                     &retval);
+            if (njs_slow_path(ret == NJS_ERROR)) {
+                return ret;
+            }
+
+            throw = njs_bool(&retval);
+        }
+    }
+
+    ret = ((magic >> 2) == NJS_FS_STAT) ? stat(path, &sb) : lstat(path, &sb);
+    if (njs_slow_path(ret != 0)) {
+        if (errno != ENOENT || throw) {
+            ret = njs_fs_error(vm,
+                               ((magic >> 2) == NJS_FS_STAT) ? "stat" : "lstat",
+                               strerror(errno), path, errno, &retval);
+            if (njs_slow_path(ret != NJS_OK)) {
+                return NJS_ERROR;
+            }
+        } else {
+            njs_set_undefined(&retval);
+        }
+
+        return njs_fs_result(vm, &retval, calltype, callback, 2);
+    }
+
+    ret = njs_fs_stats_create(vm, &sb, &retval);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return NJS_ERROR;
+    }
+
+    return njs_fs_result(vm, &retval, calltype, callback, 2);
+}
+
+
+static njs_int_t
 njs_fs_fd_read(njs_vm_t *vm, int fd, njs_str_t *data)
 {
     u_char   *p, *end, *start;
@@ -1870,6 +2017,511 @@ const njs_object_type_init_t  njs_dirent
 };
 
 
+static void
+njs_fs_to_stat(njs_stat_t *dst, struct stat *st)
+{
+    dst->st_dev = st->st_dev;
+    dst->st_mode = st->st_mode;
+    dst->st_nlink = st->st_nlink;
+    dst->st_uid = st->st_uid;
+    dst->st_gid = st->st_gid;
+    dst->st_rdev = st->st_rdev;
+    dst->st_ino = st->st_ino;
+    dst->st_size = st->st_size;
+    dst->st_blksize = st->st_blksize;
+    dst->st_blocks = st->st_blocks;
+
+#if (NJS_HAVE_STAT_ATIMESPEC)
+
+    dst->st_atim.tv_sec = st->st_atimespec.tv_sec;
+    dst->st_atim.tv_nsec = st->st_atimespec.tv_nsec;
+    dst->st_mtim.tv_sec = st->st_mtimespec.tv_sec;
+    dst->st_mtim.tv_nsec = st->st_mtimespec.tv_nsec;
+    dst->st_ctim.tv_sec = st->st_ctimespec.tv_sec;
+    dst->st_ctim.tv_nsec = st->st_ctimespec.tv_nsec;
+    dst->st_birthtim.tv_sec = st->st_birthtimespec.tv_sec;
+    dst->st_birthtim.tv_nsec = st->st_birthtimespec.tv_nsec;
+
+#elif (NJS_HAVE_STAT_ATIM)
+
+    dst->st_atim.tv_sec = st->st_atim.tv_sec;
+    dst->st_atim.tv_nsec = st->st_atim.tv_nsec;
+    dst->st_mtim.tv_sec = st->st_mtim.tv_sec;
+    dst->st_mtim.tv_nsec = st->st_mtim.tv_nsec;
+    dst->st_ctim.tv_sec = st->st_ctim.tv_sec;
+    dst->st_ctim.tv_nsec = st->st_ctim.tv_nsec;
+
+#if (NJS_HAVE_STAT_BIRTHTIM)
+    dst->st_birthtim.tv_sec = st->st_birthtim.tv_sec;
+    dst->st_birthtim.tv_nsec = st->st_birthtim.tv_nsec;
+#else
+    dst->st_birthtim.tv_sec = st->st_ctim.tv_sec;
+    dst->st_birthtim.tv_nsec = st->st_ctim.tv_nsec;
+#endif
+
+#else
+
+  dst->st_atim.tv_sec = st->st_atime;
+  dst->st_atim.tv_nsec = 0;
+  dst->st_mtim.tv_sec = st->st_mtime;
+  dst->st_mtim.tv_nsec = 0;
+  dst->st_ctim.tv_sec = st->st_ctime;
+  dst->st_ctim.tv_nsec = 0;
+  dst->st_birthtim.tv_sec = st->st_ctime;
+  dst->st_birthtim.tv_nsec = 0;
+
+#endif
+}
+
+
+static njs_int_t
+njs_fs_stats_create(njs_vm_t *vm, struct stat *st, njs_value_t *retval)
+{
+    njs_stat_t          *copy;
+    njs_object_value_t  *stat;
+
+    stat = njs_object_value_alloc(vm, NJS_OBJ_TYPE_FS_STATS, 0, NULL);
+    if (njs_slow_path(stat == NULL)) {
+        return NJS_ERROR;
+    }
+
+    stat->object.shared_hash =
+                      vm->prototypes[NJS_OBJ_TYPE_FS_STATS].object.shared_hash;
+
+    copy = njs_mp_alloc(vm->mem_pool, sizeof(njs_stat_t));
+    if (copy == NULL) {
+        njs_memory_error(vm);
+        return NJS_ERROR;
+    }
+
+    njs_fs_to_stat(copy, st);
+
+    njs_set_data(&stat->value, copy, NJS_DATA_TAG_FS_STAT);
+    njs_set_object_value(retval, stat);
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
+njs_stats_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+    njs_index_t unused)
+{
+    njs_type_error(vm, "Stats is not a constructor");
+    return NJS_ERROR;
+}
+
+
+static njs_int_t
+njs_fs_stats_test(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+    njs_index_t testtype)
+{
+    unsigned     mask;
+    njs_stat_t   *st;
+    njs_value_t  *this;
+
+    this = njs_argument(args, 0);
+
+    if (njs_slow_path(!njs_is_object_data(this, NJS_DATA_TAG_FS_STAT))) {
+        return NJS_DECLINED;
+    }
+
+    st = njs_object_data(this);
+
+    switch (testtype) {
+    case DT_DIR:
+        mask = S_IFDIR;
+        break;
+
+    case DT_REG:
+        mask = S_IFREG;
+        break;
+
+    case DT_CHR:
+        mask = S_IFCHR;
+        break;
+
+    case DT_LNK:
+        mask = S_IFLNK;
+        break;
+
+    case DT_BLK:
+        mask = S_IFBLK;
+        break;
+
+    case DT_FIFO:
+        mask = S_IFIFO;
+        break;
+
+    case DT_SOCK:
+    default:
+        mask = S_IFSOCK;
+    }
+
+    njs_set_boolean(&vm->retval, (st->st_mode & S_IFMT) == mask);
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
+njs_fs_stats_prop(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value,
+    njs_value_t *setval, njs_value_t *retval)
+{
+    double      v;
+    njs_date_t  *date;
+    njs_stat_t  *st;
+
+#define njs_fs_time_ms(ts) ((ts)->tv_sec * 1000.0 + (ts)->tv_nsec / 1000000.0)
+
+    if (njs_slow_path(!njs_is_object_data(value, NJS_DATA_TAG_FS_STAT))) {
+        return NJS_DECLINED;
+    }
+
+    st = njs_object_data(value);
+
+    switch (prop->value.data.magic16) {
+    case NJS_FS_STAT_DEV:
+        v = st->st_dev;
+        break;
+
+    case NJS_FS_STAT_INO:
+        v = st->st_ino;
+        break;
+
+    case NJS_FS_STAT_MODE:
+        v = st->st_mode;
+        break;
+
+    case NJS_FS_STAT_NLINK:
+        v = st->st_nlink;
+        break;
+
+    case NJS_FS_STAT_UID:
+        v = st->st_uid;
+        break;
+
+    case NJS_FS_STAT_GID:
+        v = st->st_gid;
+        break;
+
+    case NJS_FS_STAT_RDEV:
+        v = st->st_rdev;
+        break;
+
+    case NJS_FS_STAT_SIZE:
+        v = st->st_size;
+        break;
+
+    case NJS_FS_STAT_BLKSIZE:
+        v = st->st_blksize;
+        break;
+
+    case NJS_FS_STAT_BLOCKS:
+        v = st->st_blocks;
+        break;
+
+    case NJS_FS_STAT_ATIME:
+        v = njs_fs_time_ms(&st->st_atim);
+        break;
+
+    case NJS_FS_STAT_BIRTHTIME:
+        v = njs_fs_time_ms(&st->st_birthtim);
+        break;
+
+    case NJS_FS_STAT_CTIME:
+        v = njs_fs_time_ms(&st->st_ctim);
+        break;
+
+    case NJS_FS_STAT_MTIME:
+    default:
+        v = njs_fs_time_ms(&st->st_mtim);
+        break;
+    }
+
+    switch (prop->value.data.magic32) {
+    case NJS_NUMBER:
+        njs_set_number(retval, v);
+        break;
+
+    case NJS_DATE:
+    default:
+        date = njs_date_alloc(vm, v);
+        if (njs_slow_path(date == NULL)) {
+            return NJS_ERROR;
+        }
+
+        njs_set_date(retval, date);
+        break;
+    }
+
+    return NJS_OK;
+}
+
+
+static const njs_object_prop_t  njs_stats_constructor_properties[] =
+{
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("name"),
+        .value = njs_string("Stats"),
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("length"),
+        .value = njs_value(NJS_NUMBER, 1, 0),
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("prototype"),
+        .value = njs_prop_handler(njs_object_prototype_create),
+    },
+};
+
+
+const njs_object_init_t  njs_stats_constructor_init = {
+    njs_stats_constructor_properties,
+    njs_nitems(njs_stats_constructor_properties),
+};
+
+
+static const njs_object_prop_t  njs_stats_prototype_properties[] =
+{
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG),
+        .value = njs_string("Stats"),
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("constructor"),
+        .value = njs_prop_handler(njs_object_prototype_create_constructor),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("isBlockDevice"),
+        .value = njs_native_function2(njs_fs_stats_test, 0, DT_BLK),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_long_string("isCharacterDevice"),
+        .value = njs_native_function2(njs_fs_stats_test, 0, DT_CHR),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("isDirectory"),
+        .value = njs_native_function2(njs_fs_stats_test, 0, DT_DIR),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("isFIFO"),
+        .value = njs_native_function2(njs_fs_stats_test, 0, DT_FIFO),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("isFile"),
+        .value = njs_native_function2(njs_fs_stats_test, 0, DT_REG),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("isSocket"),
+        .value = njs_native_function2(njs_fs_stats_test, 0, DT_SOCK),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("isSymbolicLink"),
+        .value = njs_native_function2(njs_fs_stats_test, 0, DT_LNK),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("dev"),
+        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_DEV,
+                                   NJS_NUMBER),
+        .enumerable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("ino"),
+        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_INO,
+                                   NJS_NUMBER),
+        .enumerable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("mode"),
+        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_MODE,
+                                   NJS_NUMBER),
+        .enumerable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("nlink"),
+        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_NLINK,
+                                   NJS_NUMBER),
+        .enumerable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("uid"),
+        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_UID,
+                                   NJS_NUMBER),
+        .enumerable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("gid"),
+        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_GID,
+                                   NJS_NUMBER),
+        .enumerable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("rdev"),
+        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_RDEV,
+                                   NJS_NUMBER),
+        .enumerable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("size"),
+        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_SIZE,
+                                   NJS_NUMBER),
+        .enumerable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("blksize"),
+        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_BLKSIZE,
+                                   NJS_NUMBER),
+        .enumerable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("blocks"),
+        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_BLOCKS,
+                                   NJS_NUMBER),
+        .enumerable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("atimeMs"),
+        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_ATIME,
+                                   NJS_NUMBER),
+        .enumerable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("birthtimeMs"),
+        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_BIRTHTIME,
+                                   NJS_NUMBER),
+        .enumerable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("ctimeMs"),
+        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_CTIME,
+                                   NJS_NUMBER),
+        .enumerable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("mtimeMs"),
+        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_MTIME,
+                                   NJS_NUMBER),
+        .enumerable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("atime"),
+        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_ATIME,
+                                   NJS_DATE),
+        .enumerable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("birthtime"),
+        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_BIRTHTIME,
+                                   NJS_DATE),
+        .enumerable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("ctime"),
+        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_CTIME,
+                                   NJS_DATE),
+        .enumerable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("mtime"),
+        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_MTIME,
+                                   NJS_DATE),
+        .enumerable = 1,
+    },
+};
+
+
+const njs_object_init_t  njs_stats_prototype_init = {
+    njs_stats_prototype_properties,
+    njs_nitems(njs_stats_prototype_properties),
+};
+
+
+const njs_object_type_init_t  njs_stats_type_init = {
+    .constructor = njs_native_ctor(njs_stats_constructor, 0, 0),
+    .prototype_props = &njs_stats_prototype_init,
+    .constructor_props = &njs_stats_constructor_init,
+    .prototype_value = { .object = { .type = NJS_OBJECT } },
+};
+
+
 static const njs_object_prop_t  njs_fs_promises_properties[] =
 {
     {
@@ -1940,6 +2592,24 @@ static const njs_object_prop_t  njs_fs_p
 
     {
         .type = NJS_PROPERTY,
+        .name = njs_string("lstat"),
+        .value = njs_native_function2(njs_fs_stat, 0,
+                                   njs_fs_magic(NJS_FS_PROMISE, NJS_FS_LSTAT)),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("stat"),
+        .value = njs_native_function2(njs_fs_stat, 0,
+                                    njs_fs_magic(NJS_FS_PROMISE, NJS_FS_STAT)),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
         .name = njs_string("symlink"),
         .value = njs_native_function2(njs_fs_symlink, 0, NJS_FS_PROMISE),
         .writable = 1,
@@ -2231,6 +2901,42 @@ static const njs_object_prop_t  njs_fs_o
         .writable = 1,
         .configurable = 1,
     },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("lstat"),
+        .value = njs_native_function2(njs_fs_stat, 0,
+                                  njs_fs_magic(NJS_FS_CALLBACK, NJS_FS_LSTAT)),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("lstatSync"),
+        .value = njs_native_function2(njs_fs_stat, 0,
+                                    njs_fs_magic(NJS_FS_DIRECT, NJS_FS_LSTAT)),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("stat"),
+        .value = njs_native_function2(njs_fs_stat, 0,
+                                   njs_fs_magic(NJS_FS_CALLBACK, NJS_FS_STAT)),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("statSync"),
+        .value = njs_native_function2(njs_fs_stat, 0,
+                                     njs_fs_magic(NJS_FS_DIRECT, NJS_FS_STAT)),
+        .writable = 1,
+        .configurable = 1,
+    },
 };
 
 
diff -r 4ddfb2f2227f -r 203bc61d8d70 src/njs_fs.h
--- a/src/njs_fs.h	Tue Nov 02 12:38:42 2021 +0000
+++ b/src/njs_fs.h	Wed Nov 03 15:46:15 2021 +0000
@@ -11,5 +11,6 @@
 extern const njs_object_init_t  njs_fs_object_init;
 
 extern const njs_object_type_init_t  njs_dirent_type_init;
+extern const njs_object_type_init_t  njs_stats_type_init;
 
 #endif /* _NJS_FS_H_INCLUDED_ */
diff -r 4ddfb2f2227f -r 203bc61d8d70 src/njs_value.h
--- a/src/njs_value.h	Tue Nov 02 12:38:42 2021 +0000
+++ b/src/njs_value.h	Wed Nov 03 15:46:15 2021 +0000
@@ -72,6 +72,7 @@ typedef enum {
     NJS_DATA_TAG_TEXT_ENCODER,
     NJS_DATA_TAG_TEXT_DECODER,
     NJS_DATA_TAG_ARRAY_ITERATOR,
+    NJS_DATA_TAG_FS_STAT,
     NJS_DATA_TAG_MAX
 } njs_data_tag_t;
 
diff -r 4ddfb2f2227f -r 203bc61d8d70 src/njs_vm.h
--- a/src/njs_vm.h	Tue Nov 02 12:38:42 2021 +0000
+++ b/src/njs_vm.h	Wed Nov 03 15:46:15 2021 +0000
@@ -57,6 +57,7 @@ typedef enum {
     NJS_OBJ_TYPE_ITERATOR,
     NJS_OBJ_TYPE_ARRAY_ITERATOR,
     NJS_OBJ_TYPE_FS_DIRENT,
+    NJS_OBJ_TYPE_FS_STATS,
     NJS_OBJ_TYPE_CRYPTO_HASH,
     NJS_OBJ_TYPE_CRYPTO_HMAC,
     NJS_OBJ_TYPE_TYPED_ARRAY,
diff -r 4ddfb2f2227f -r 203bc61d8d70 test/fs/methods.js
--- a/test/fs/methods.js	Tue Nov 02 12:38:42 2021 +0000
+++ b/test/fs/methods.js	Wed Nov 03 15:46:15 2021 +0000
@@ -355,6 +355,186 @@ let realpathP_tsuite = {
     tests: realpath_tests,
 };
 
+async function stat_test(params) {
+    if (params.init) {
+        params.init(params);
+    }
+
+    let stat = await method(params.method, params).catch(e => ({error:e}));
+
+    if (params.check && !params.check(stat, params)) {
+        throw Error(`${params.method} failed check`);
+    }
+
+    return 'SUCCESS';
+}
+
+function contains(arr, elts) {
+    return elts.every(el => {
+        let r = arr.some(v => el == v);
+
+        if (!r) {
+            throw Error(`${el} is not found`);
+        }
+
+        return r;
+    });
+}
+
+let stat_tests = [
+    { args: ["/invalid_path"],
+      check: (err, params) => {
+          let e = err.error;
+
+          if (e.syscall != params.method) {
+              throw Error(`${e.syscall} unexpected syscall`);
+          }
+
+          if (e.code != "ENOENT") {
+              throw Error(`${e.code} unexpected code`);
+          }
+
+          return true;
+      } },
+
+    { args: ["@_link"],
+      init: (params) => {
+        let lname = params.args[0];
+        let fname = lname.slice(0, -5);
+
+        /* making symbolic link. */
+
+        try { fs.unlinkSync(fname); fs.unlinkSync(lname); } catch (e) {}
+
+        fs.writeFileSync(fname, fname);
+
+        fname = fs.realpathSync(fname);
+        fs.symlinkSync(fname, lname);
+      },
+
+      check: (st, params) => {
+          switch (params.method) {
+          case "stat":
+              if (!st.isFile()) {
+                  throw Error(`${params.args[0]} is not a file`);
+              }
+
+              break;
+
+          case "lstat":
+              if (!st.isSymbolicLink()) {
+                  throw Error(`${params.args[0]} is not a link`);
+              }
+
+              break;
+          }
+
+          return true;
+      } },
+
+    { args: ["./build/"],
+      check: (st) => contains(Object.keys(st),
+                              [ "atime", "atimeMs", "birthtime", "birthtimeMs",
+                                "blksize", "blocks", "ctime", "ctimeMs", "dev",
+                                "gid", "ino", "mode", "mtime", "mtimeMs","nlink",
+                                "rdev", "size", "uid" ]) },
+
+    { args: ["./build/"],
+      check: (st) => Object.keys(st).every(p => {
+        let v = st[p];
+        if (p == 'atime' || p == 'ctime' || p == 'mtime' || p == 'birthtime') {
+            if (!(v instanceof Date)) {
+                throw Error(`${p} is not an instance of Date`);
+            }
+
+            return true;
+        }
+


More information about the nginx-devel mailing list